mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-21 19:34:40 +08:00
Merge recipe -> dev
--HG-- branch : dev
This commit is contained in:
38
src/Orchard.Web/Core/Navigation/Commands/MenuCommands.cs
Normal file
38
src/Orchard.Web/Core/Navigation/Commands/MenuCommands.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using Orchard.Commands;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Core.Navigation.Models;
|
||||
|
||||
namespace Orchard.Core.Navigation.Commands {
|
||||
public class MenuCommands : DefaultOrchardCommandHandler {
|
||||
private readonly IContentManager _contentManager;
|
||||
|
||||
public MenuCommands(IContentManager contentManager) {
|
||||
_contentManager = contentManager;
|
||||
}
|
||||
|
||||
[OrchardSwitch]
|
||||
public string MenuPosition { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public string MenuText { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public string Url { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public bool OnMainMenu { get; set; }
|
||||
|
||||
[CommandName("menuitem create")]
|
||||
[CommandHelp("menuitem create /MenuPosition:<position> /MenuText:<text> /Url:<url> [/OnMainMenu:true|false]\r\n\t" + "Creates a new menu item")]
|
||||
[OrchardSwitches("MenuPosition,MenuText,Url,OnMainMenu")]
|
||||
public void Create() {
|
||||
var menuItem = _contentManager.Create("MenuItem");
|
||||
menuItem.As<MenuPart>().MenuPosition = MenuPosition;
|
||||
menuItem.As<MenuPart>().MenuText = T(MenuText).ToString();
|
||||
menuItem.As<MenuPart>().OnMainMenu = OnMainMenu;
|
||||
menuItem.As<MenuItemPart>().Url = Url;
|
||||
|
||||
Context.Output.WriteLine(T("Menu item created successfully.").Text);
|
||||
}
|
||||
}
|
||||
}
|
@@ -104,6 +104,7 @@
|
||||
<Compile Include="Contents\Settings\ContentPartSettings.cs" />
|
||||
<Compile Include="Contents\Shapes.cs" />
|
||||
<Compile Include="Contents\ViewModels\PublishContentViewModel.cs" />
|
||||
<Compile Include="Navigation\Commands\MenuCommands.cs" />
|
||||
<Compile Include="Navigation\Services\MainMenuNavigationProvider.cs" />
|
||||
<Compile Include="Routable\Events\ISlugEventHandler.cs" />
|
||||
<Compile Include="Routable\ResourceManifest.cs" />
|
||||
|
@@ -2,6 +2,7 @@
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml.Linq;
|
||||
using Orchard.Blogs.Models;
|
||||
using Orchard.Commands;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Aspects;
|
||||
@@ -12,6 +13,7 @@ using Orchard.Core.Routable.Services;
|
||||
using Orchard.Security;
|
||||
using Orchard.Blogs.Services;
|
||||
using Orchard.Core.Navigation.Services;
|
||||
using Orchard.Settings;
|
||||
|
||||
namespace Orchard.Blogs.Commands {
|
||||
public class BlogCommands : DefaultOrchardCommandHandler {
|
||||
@@ -19,16 +21,19 @@ namespace Orchard.Blogs.Commands {
|
||||
private readonly IMembershipService _membershipService;
|
||||
private readonly IBlogService _blogService;
|
||||
private readonly IMenuService _menuService;
|
||||
private readonly ISiteService _siteService;
|
||||
|
||||
public BlogCommands(
|
||||
IContentManager contentManager,
|
||||
IMembershipService membershipService,
|
||||
IBlogService blogService,
|
||||
IMenuService menuService) {
|
||||
IMenuService menuService,
|
||||
ISiteService siteService) {
|
||||
_contentManager = contentManager;
|
||||
_membershipService = membershipService;
|
||||
_blogService = blogService;
|
||||
_menuService = menuService;
|
||||
_siteService = siteService;
|
||||
}
|
||||
|
||||
[OrchardSwitch]
|
||||
@@ -43,13 +48,22 @@ namespace Orchard.Blogs.Commands {
|
||||
[OrchardSwitch]
|
||||
public string Title { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public string Description { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public string MenuText { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public bool Homepage { get; set; }
|
||||
|
||||
[CommandName("blog create")]
|
||||
[CommandHelp("blog create /Slug:<slug> /Title:<title> /Owner:<username> [/MenuText:<menu text>]\r\n\t" + "Creates a new Blog")]
|
||||
[OrchardSwitches("Slug,Title,Owner,MenuText")]
|
||||
[CommandHelp("blog create /Slug:<slug> /Title:<title> [/Owner:<username>] [/Description:<description>] [/MenuText:<menu text>] [/Homepage:true|false]\r\n\t" + "Creates a new Blog")]
|
||||
[OrchardSwitches("Slug,Title,Owner,Description,MenuText,Homepage")]
|
||||
public string Create() {
|
||||
if (String.IsNullOrEmpty(Owner)) {
|
||||
Owner = _siteService.GetSiteSettings().SuperUser;
|
||||
}
|
||||
var owner = _membershipService.GetUser(Owner);
|
||||
|
||||
if ( owner == null ) {
|
||||
@@ -65,6 +79,10 @@ namespace Orchard.Blogs.Commands {
|
||||
blog.As<RoutePart>().Slug = Slug;
|
||||
blog.As<RoutePart>().Path = Slug;
|
||||
blog.As<RoutePart>().Title = Title;
|
||||
blog.As<RoutePart>().PromoteToHomePage = Homepage;
|
||||
if (!String.IsNullOrEmpty(Description)) {
|
||||
blog.As<BlogPart>().Description = Description;
|
||||
}
|
||||
if ( !String.IsNullOrWhiteSpace(MenuText) ) {
|
||||
blog.As<MenuPart>().OnMainMenu = true;
|
||||
blog.As<MenuPart>().MenuPosition = _menuService.Get().Select(menuPart => menuPart.MenuPosition).Max() + 1 + ".0";
|
||||
|
@@ -2,5 +2,24 @@
|
||||
|
||||
namespace Orchard.Comments.Models {
|
||||
public class CommentSettingsPart : ContentPart<CommentSettingsPartRecord> {
|
||||
public bool ModerateComments {
|
||||
get { return Record.ModerateComments; }
|
||||
set { Record.ModerateComments = value; }
|
||||
}
|
||||
|
||||
public bool EnableSpamProtection {
|
||||
get { return Record.EnableSpamProtection; }
|
||||
set { Record.EnableSpamProtection = value; }
|
||||
}
|
||||
|
||||
public string AkismetKey {
|
||||
get { return Record.AkismetKey; }
|
||||
set { Record.AkismetKey = value; }
|
||||
}
|
||||
|
||||
public string AkismetUrl {
|
||||
get { return Record.AkismetUrl; }
|
||||
set { Record.AkismetUrl = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using Orchard.Commands;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Aspects;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Core.Routable.Models;
|
||||
using Orchard.Security;
|
||||
using Orchard.Settings;
|
||||
|
||||
namespace Orchard.Pages.Commands {
|
||||
public class PageCommands : DefaultOrchardCommandHandler {
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly IMembershipService _membershipService;
|
||||
private readonly ISiteService _siteService;
|
||||
|
||||
public PageCommands(IContentManager contentManager, IMembershipService membershipService, ISiteService siteService) {
|
||||
_contentManager = contentManager;
|
||||
_membershipService = membershipService;
|
||||
_siteService = siteService;
|
||||
}
|
||||
|
||||
[OrchardSwitch]
|
||||
public string Slug { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public string Title { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public string Path { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public string Text { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public string Owner { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public bool Homepage { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public bool Publish { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public bool UseWelcomeText { get; set; }
|
||||
|
||||
[CommandName("page create")]
|
||||
[CommandHelp("page create /Slug:<slug> /Title:<title> /Path:<path> [/Text:<text>] [/Owner:<username>] [/Homepage:true|false] [/Publish:true|false] [/UseWelcomeText:true|false]\r\n\t" + "Creates a new page")]
|
||||
[OrchardSwitches("Slug,Title,Path,Text,Owner,Homepage,Publish,UseWelcomeText")]
|
||||
public void Create() {
|
||||
if (String.IsNullOrEmpty(Owner)) {
|
||||
Owner = _siteService.GetSiteSettings().SuperUser;
|
||||
}
|
||||
var owner = _membershipService.GetUser(Owner);
|
||||
var page = _contentManager.Create("Page", VersionOptions.Draft);
|
||||
page.As<RoutePart>().Title = Title;
|
||||
page.As<RoutePart>().Path = Path;
|
||||
page.As<RoutePart>().Slug = Slug;
|
||||
page.As<RoutePart>().PromoteToHomePage = Homepage;
|
||||
page.As<ICommonPart>().Owner = owner;
|
||||
var text = String.Empty;
|
||||
if (UseWelcomeText) {
|
||||
text = T(
|
||||
@"<p>You've successfully setup your Orchard Site and this is the homepage of your new site.
|
||||
Here are a few things you can look at to get familiar with the application.
|
||||
Once you feel confident you don't need this anymore, you can
|
||||
<a href=""Admin/Contents/Edit/{0}"">remove it by going into editing mode</a>
|
||||
and replacing it with whatever you want.</p>
|
||||
<p>First things first - You'll probably want to <a href=""Admin/Settings"">manage your settings</a>
|
||||
and configure Orchard to your liking. After that, you can head over to
|
||||
<a href=""Admin/Themes"">manage themes to change or install new themes</a>
|
||||
and really make it your own. Once you're happy with a look and feel, it's time for some content.
|
||||
You can start creating new custom content types or start from the built-in ones by
|
||||
<a href=""Admin/Contents/Create/Page"">adding a page</a>, or <a href=""Admin/Navigation"">managing your menus.</a></p>
|
||||
<p>Finally, Orchard has been designed to be extended. It comes with a few built-in
|
||||
modules such as pages and blogs or themes. If you're looking to add additional functionality,
|
||||
you can do so by creating your own module or by installing one that somebody else built.
|
||||
Modules are created by other users of Orchard just like you so if you feel up to it,
|
||||
<a href=""http://orchardproject.net/contribution"">please consider participating</a>.</p>
|
||||
<p>Thanks for using Orchard – The Orchard Team </p>", page.Id).Text;
|
||||
}
|
||||
else {
|
||||
if (!String.IsNullOrEmpty(Text)) {
|
||||
text = Text;
|
||||
}
|
||||
}
|
||||
page.As<BodyPart>().Text = text;
|
||||
if (Publish) {
|
||||
_contentManager.Publish(page);
|
||||
}
|
||||
|
||||
Context.Output.WriteLine(T("Page created successfully.").Text);
|
||||
}
|
||||
}
|
||||
}
|
@@ -38,6 +38,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AdminMenu.cs" />
|
||||
<Compile Include="Commands\PageCommands.cs" />
|
||||
<Compile Include="Migrations.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
|
@@ -0,0 +1,23 @@
|
||||
using System.Web.Mvc;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Recipes.Services;
|
||||
|
||||
namespace Orchard.Recipes.Controllers {
|
||||
public class RecipesController : Controller {
|
||||
private readonly IRecipeJournal _recipeJournal;
|
||||
|
||||
public RecipesController(IRecipeJournal recipeJournal) {
|
||||
_recipeJournal = recipeJournal;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public ActionResult RecipeExecutionStatus (string executionId) {
|
||||
var recipeStatus = _recipeJournal.GetRecipeStatus(executionId);
|
||||
var model = recipeStatus;
|
||||
|
||||
return View(model);
|
||||
}
|
||||
}
|
||||
}
|
12
src/Orchard.Web/Modules/Orchard.Recipes/Module.txt
Normal file
12
src/Orchard.Web/Modules/Orchard.Recipes/Module.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
Name: Recipes
|
||||
AntiForgery: enabled
|
||||
Author: The Orchard Team
|
||||
Website: http://orchardproject.net
|
||||
Version: 1.0.20
|
||||
OrchardVersion: 1.0.20
|
||||
Description: Provides Orchard Recipes.
|
||||
Features:
|
||||
Orchard.Recipes:
|
||||
Name: Recipes
|
||||
Description: Implementation of Orchard recipes.
|
||||
Category: Core
|
118
src/Orchard.Web/Modules/Orchard.Recipes/Orchard.Recipes.csproj
Normal file
118
src/Orchard.Web/Modules/Orchard.Recipes/Orchard.Recipes.csproj
Normal file
@@ -0,0 +1,118 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>
|
||||
</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{FC1D74E8-7A4D-48F4-83DE-95C6173780C4}</ProjectGuid>
|
||||
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Orchard.Recipes</RootNamespace>
|
||||
<AssemblyName>Orchard.Recipes</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<CodeAnalysisRuleSet>..\..\..\OrchardBasicCorrectness.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Autofac">
|
||||
<HintPath>..\..\..\..\lib\autofac\Autofac.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\..\lib\aspnetmvc\System.Web.Mvc.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Configuration" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Module.txt" />
|
||||
<Content Include="web.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Controllers\RecipesController.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="RecipeHandlers\CommandRecipeHandler.cs" />
|
||||
<Compile Include="RecipeHandlers\FeatureRecipeHandler.cs" />
|
||||
<Compile Include="RecipeHandlers\MetadataRecipeHandler.cs" />
|
||||
<Compile Include="RecipeHandlers\MigrationRecipeHandler.cs" />
|
||||
<Compile Include="RecipeHandlers\ModuleRecipeHandler.cs" />
|
||||
<Compile Include="RecipeHandlers\SettingsRecipeHandler.cs" />
|
||||
<Compile Include="RecipeHandlers\ThemeRecipeHandler.cs" />
|
||||
<Compile Include="Routes.cs" />
|
||||
<Compile Include="Services\RecipeHarvester.cs" />
|
||||
<Compile Include="Services\RecipeJournalManager.cs" />
|
||||
<Compile Include="Services\RecipeManager.cs" />
|
||||
<Compile Include="Services\RecipeParser.cs" />
|
||||
<Compile Include="Services\RecipeScheduler.cs" />
|
||||
<Compile Include="Services\RecipeStepExecutor.cs" />
|
||||
<Compile Include="Services\RecipeStepQueue.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\Orchard\Orchard.Framework.csproj">
|
||||
<Project>{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}</Project>
|
||||
<Name>Orchard.Framework</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard.Modules\Orchard.Modules.csproj">
|
||||
<Project>{17F86780-9A1F-4AA1-86F1-875EEC2730C7}</Project>
|
||||
<Name>Orchard.Modules</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard.Packaging\Orchard.Packaging.csproj">
|
||||
<Project>{DFD137A2-DDB5-4D22-BE0D-FA9AD4C8B059}</Project>
|
||||
<Name>Orchard.Packaging</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard.Themes\Orchard.Themes.csproj">
|
||||
<Project>{CDE24A24-01D3-403C-84B9-37722E18DFB7}</Project>
|
||||
<Name>Orchard.Themes</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
|
||||
<WebProjectProperties>
|
||||
<UseIIS>False</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>57675</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>
|
||||
</IISUrl>
|
||||
<NTLMAuthentication>False</NTLMAuthentication>
|
||||
<UseCustomServer>True</UseCustomServer>
|
||||
<CustomServerUrl>http://orchard.codeplex.com/</CustomServerUrl>
|
||||
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
|
||||
</WebProjectProperties>
|
||||
</FlavorProperties>
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
@@ -0,0 +1,35 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Orchard.Recipes")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyProduct("Orchard")]
|
||||
[assembly: AssemblyCopyright("Copyright © Outercurve Foundation 2009")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("9f7f12bd-c74c-48c0-a378-c9d26dfe2c5c")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Revision and Build Numbers
|
||||
// by using the '*' as shown below:
|
||||
[assembly: AssemblyVersion("1.0.20")]
|
||||
[assembly: AssemblyFileVersion("1.0.20")]
|
||||
[assembly: SecurityTransparent]
|
@@ -0,0 +1,201 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Orchard.Commands;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Recipes.Models;
|
||||
using Orchard.Recipes.Services;
|
||||
|
||||
namespace Orchard.Recipes.RecipeHandlers {
|
||||
public class CommandRecipeHandler : IRecipeHandler {
|
||||
private readonly ICommandManager _commandManager;
|
||||
private readonly CommandParser _commandParser;
|
||||
|
||||
public CommandRecipeHandler (ICommandManager commandManager) {
|
||||
_commandManager = commandManager;
|
||||
_commandParser = new CommandParser();
|
||||
Logger = NullLogger.Instance;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
ILogger Logger { get; set; }
|
||||
|
||||
/*
|
||||
<Command>
|
||||
command1
|
||||
command2
|
||||
command3
|
||||
</Command>
|
||||
*/
|
||||
// run Orchard 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;
|
||||
}
|
||||
}
|
||||
|
||||
// Utility class for parsing lines of commands.
|
||||
// Note: This lexer handles double quotes pretty harshly by design.
|
||||
// In case you needed them in your arguments, hopefully single quotes work for you as a replacement on the receiving end.
|
||||
class CommandParser {
|
||||
public CommandParameters ParseCommandParameters(string command) {
|
||||
var args = SplitArgs(command);
|
||||
var arguments = new List<string>();
|
||||
var result = new CommandParameters {
|
||||
Switches = new Dictionary<string, string>()
|
||||
};
|
||||
|
||||
foreach (var arg in args) {
|
||||
// Switch?
|
||||
if (arg[0] == '/') {
|
||||
int index = arg.IndexOf(':');
|
||||
var switchName = (index < 0 ? arg.Substring(1) : arg.Substring(1, index - 1));
|
||||
var switchValue = (index < 0 || index >= arg.Length ? string.Empty : arg.Substring(index + 1));
|
||||
|
||||
if (string.IsNullOrEmpty(switchName))
|
||||
{
|
||||
throw new ArgumentException(string.Format("Invalid switch syntax: \"{0}\". Valid syntax is /<switchName>[:<switchValue>].", arg));
|
||||
}
|
||||
|
||||
result.Switches.Add(switchName, switchValue);
|
||||
}
|
||||
else {
|
||||
arguments.Add(arg);
|
||||
}
|
||||
}
|
||||
|
||||
result.Arguments = arguments;
|
||||
return result;
|
||||
}
|
||||
|
||||
class State {
|
||||
private readonly string _commandLine;
|
||||
private readonly StringBuilder _stringBuilder;
|
||||
private readonly List<string> _arguments;
|
||||
private int _index;
|
||||
|
||||
public State(string commandLine) {
|
||||
_commandLine = commandLine;
|
||||
_stringBuilder = new StringBuilder();
|
||||
_arguments = new List<string>();
|
||||
}
|
||||
|
||||
public StringBuilder StringBuilder { get { return _stringBuilder; } }
|
||||
public bool EOF { get { return _index >= _commandLine.Length; } }
|
||||
public char Current { get { return _commandLine[_index]; } }
|
||||
public IEnumerable<string> Arguments { get { return _arguments; } }
|
||||
|
||||
public void AddArgument() {
|
||||
_arguments.Add(StringBuilder.ToString());
|
||||
StringBuilder.Clear();
|
||||
}
|
||||
|
||||
public void AppendCurrent() {
|
||||
StringBuilder.Append(Current);
|
||||
}
|
||||
|
||||
public void Append(char ch) {
|
||||
StringBuilder.Append(ch);
|
||||
}
|
||||
|
||||
public void MoveNext() {
|
||||
if (!EOF)
|
||||
_index++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implement the same logic as found at
|
||||
/// http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
|
||||
/// The 3 special characters are quote, backslash and whitespaces, in order
|
||||
/// of priority.
|
||||
/// The semantics of a quote is: whatever the state of the lexer, copy
|
||||
/// all characters verbatim until the next quote or EOF.
|
||||
/// The semantics of backslash is: If the next character is a backslash or a quote,
|
||||
/// copy the next character. Otherwise, copy the backslash and the next character.
|
||||
/// The semantics of whitespace is: end the current argument and move on to the next one.
|
||||
/// </summary>
|
||||
private static IEnumerable<string> SplitArgs(string commandLine) {
|
||||
var state = new State(commandLine);
|
||||
while (!state.EOF) {
|
||||
switch (state.Current) {
|
||||
case '"':
|
||||
ProcessQuote(state);
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
ProcessBackslash(state);
|
||||
break;
|
||||
|
||||
case ' ':
|
||||
case '\t':
|
||||
if (state.StringBuilder.Length > 0)
|
||||
state.AddArgument();
|
||||
state.MoveNext();
|
||||
break;
|
||||
|
||||
default:
|
||||
state.AppendCurrent();
|
||||
state.MoveNext();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (state.StringBuilder.Length > 0)
|
||||
state.AddArgument();
|
||||
return state.Arguments;
|
||||
}
|
||||
|
||||
private static void ProcessQuote(State state) {
|
||||
state.MoveNext();
|
||||
while (!state.EOF) {
|
||||
if (state.Current == '"') {
|
||||
state.MoveNext();
|
||||
break;
|
||||
}
|
||||
state.AppendCurrent();
|
||||
state.MoveNext();
|
||||
}
|
||||
|
||||
state.AddArgument();
|
||||
}
|
||||
|
||||
private static void ProcessBackslash(State state) {
|
||||
state.MoveNext();
|
||||
if (state.EOF) {
|
||||
state.Append('\\');
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.Current == '"') {
|
||||
state.Append('"');
|
||||
state.MoveNext();
|
||||
}
|
||||
else {
|
||||
state.Append('\\');
|
||||
state.AppendCurrent();
|
||||
state.MoveNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.Environment.Features;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Modules.Services;
|
||||
using Orchard.Recipes.Models;
|
||||
using Orchard.Recipes.Services;
|
||||
|
||||
namespace Orchard.Recipes.RecipeHandlers {
|
||||
public class FeatureRecipeHandler : IRecipeHandler {
|
||||
private readonly IFeatureManager _featureManager;
|
||||
private readonly IModuleService _moduleService;
|
||||
|
||||
public FeatureRecipeHandler(IFeatureManager featureManager, IModuleService moduleService) {
|
||||
_featureManager = featureManager;
|
||||
_moduleService = moduleService;
|
||||
Logger = NullLogger.Instance;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
ILogger Logger { get; set; }
|
||||
|
||||
// <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) {
|
||||
_moduleService.DisableFeatures(featuresToDisable, true);
|
||||
}
|
||||
if (featuresToEnable.Count != 0) {
|
||||
_moduleService.EnableFeatures(featuresToEnable, true);
|
||||
}
|
||||
|
||||
recipeContext.Executed = true;
|
||||
}
|
||||
|
||||
private static List<string> ParseFeatures(string csv) {
|
||||
return csv.Split(',')
|
||||
.Select(value => value.Trim())
|
||||
.Where(sanitizedValue => !String.IsNullOrEmpty(sanitizedValue))
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Xml;
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Recipes.Models;
|
||||
using Orchard.Recipes.Services;
|
||||
|
||||
namespace Orchard.Recipes.RecipeHandlers {
|
||||
public class MetadataRecipeHandler : IRecipeHandler {
|
||||
private readonly IContentDefinitionManager _contentDefinitionManager;
|
||||
private readonly IContentDefinitionReader _contentDefinitionReader;
|
||||
|
||||
public MetadataRecipeHandler(IContentDefinitionManager contentDefinitionManager, IContentDefinitionReader contentDefinitionReader) {
|
||||
_contentDefinitionManager = contentDefinitionManager;
|
||||
_contentDefinitionReader = contentDefinitionReader;
|
||||
Logger = NullLogger.Instance;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
ILogger Logger { get; set; }
|
||||
|
||||
/*
|
||||
<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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.Data.Migration;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Recipes.Models;
|
||||
using Orchard.Recipes.Services;
|
||||
|
||||
namespace Orchard.Recipes.RecipeHandlers {
|
||||
public class MigrationRecipeHandler : IRecipeHandler {
|
||||
private readonly IDataMigrationManager _dataMigrationManager;
|
||||
|
||||
public MigrationRecipeHandler(IDataMigrationManager dataMigrationManager) {
|
||||
_dataMigrationManager = dataMigrationManager;
|
||||
Logger = NullLogger.Instance;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
ILogger Logger { get; set; }
|
||||
|
||||
// <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;
|
||||
}
|
||||
|
||||
private static List<string> ParseFeatures(string csv) {
|
||||
return csv.Split(',')
|
||||
.Select(value => value.Trim())
|
||||
.Where(sanitizedValue => !String.IsNullOrEmpty(sanitizedValue))
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Web.Hosting;
|
||||
using Orchard.Data.Migration;
|
||||
using Orchard.Environment.Extensions;
|
||||
using Orchard.Environment.Extensions.Models;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Modules.Services;
|
||||
using Orchard.Packaging.Models;
|
||||
using Orchard.Packaging.Services;
|
||||
using Orchard.Recipes.Models;
|
||||
using Orchard.Recipes.Services;
|
||||
|
||||
namespace Orchard.Recipes.RecipeHandlers {
|
||||
public class ModuleRecipeHandler : IRecipeHandler {
|
||||
private readonly IPackagingSourceManager _packagingSourceManager;
|
||||
private readonly IPackageManager _packageManager;
|
||||
private readonly IExtensionManager _extensionManager;
|
||||
private readonly IModuleService _moduleService;
|
||||
private readonly IDataMigrationManager _dataMigrationManager;
|
||||
|
||||
public ModuleRecipeHandler(
|
||||
IPackagingSourceManager packagingSourceManager,
|
||||
IPackageManager packageManager,
|
||||
IExtensionManager extensionManager,
|
||||
IModuleService moduleService,
|
||||
IDataMigrationManager dataMigrationManager) {
|
||||
_packagingSourceManager = packagingSourceManager;
|
||||
_packageManager = packageManager;
|
||||
_extensionManager = extensionManager;
|
||||
_moduleService = moduleService;
|
||||
_dataMigrationManager = dataMigrationManager;
|
||||
Logger = NullLogger.Instance;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
ILogger Logger { get; set; }
|
||||
|
||||
// <Module name="module1" [repository="somerepo"] version="1.1" />
|
||||
// install modules from feed.
|
||||
public void ExecuteRecipeStep(RecipeContext recipeContext) {
|
||||
if (!String.Equals(recipeContext.RecipeStep.Name, "Module", StringComparison.OrdinalIgnoreCase)) {
|
||||
return;
|
||||
}
|
||||
string name = null, version = null, repository = null;
|
||||
|
||||
foreach (var attribute in recipeContext.RecipeStep.Step.Attributes()) {
|
||||
if (String.Equals(attribute.Name.LocalName, "name", StringComparison.OrdinalIgnoreCase)) {
|
||||
name = attribute.Value;
|
||||
}
|
||||
else if (String.Equals(attribute.Name.LocalName, "version", StringComparison.OrdinalIgnoreCase)) {
|
||||
version = attribute.Value;
|
||||
}
|
||||
else if (String.Equals(attribute.Name.LocalName, "repository", StringComparison.OrdinalIgnoreCase)) {
|
||||
repository = attribute.Value;
|
||||
}
|
||||
else {
|
||||
throw new InvalidOperationException(string.Format("Unrecognized attribute {0} encountered in step Module.", attribute.Name.LocalName));
|
||||
}
|
||||
}
|
||||
|
||||
if (name == null) {
|
||||
throw new InvalidOperationException("Name 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.
|
||||
bool enforceVersion = version != null;
|
||||
bool installed = false;
|
||||
PackagingSource packagingSource = _packagingSourceManager.GetSources().FirstOrDefault();
|
||||
if (repository != null) {
|
||||
enforceVersion = false;
|
||||
packagingSource = new PackagingSource {FeedTitle = repository, FeedUrl = repository};
|
||||
}
|
||||
foreach (var packagingEntry in _packagingSourceManager.GetExtensionList(packagingSource)) {
|
||||
if (String.Equals(packagingEntry.Title, name, StringComparison.OrdinalIgnoreCase)) {
|
||||
if (enforceVersion && !String.Equals(packagingEntry.Version, version, StringComparison.OrdinalIgnoreCase)) {
|
||||
continue;
|
||||
}
|
||||
var extensions = _extensionManager.AvailableExtensions();
|
||||
if (extensions.Where(extension =>
|
||||
DefaultExtensionTypes.IsModule(extension.ExtensionType) &&
|
||||
String.Equals(packagingEntry.Title, extension.Name, StringComparison.OrdinalIgnoreCase)).Any()) {
|
||||
throw new InvalidOperationException(string.Format("Module {0} already exists.", name));
|
||||
}
|
||||
_packageManager.Install(packagingEntry.PackageId, packagingEntry.Version, packagingSource.FeedUrl, HostingEnvironment.MapPath("~/"));
|
||||
foreach (
|
||||
var features in
|
||||
from extensionDescriptor in extensions
|
||||
where String.Equals(extensionDescriptor.Name, packagingEntry.Title, StringComparison.OrdinalIgnoreCase)
|
||||
select extensionDescriptor.Features.Select(f => f.Name).ToArray()) {
|
||||
_moduleService.EnableFeatures(features);
|
||||
_dataMigrationManager.Update(features);
|
||||
installed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!installed) {
|
||||
throw new InvalidOperationException(string.Format("Module {0} was not found in the specified location.", name));
|
||||
}
|
||||
|
||||
|
||||
recipeContext.Executed = true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Recipes.Models;
|
||||
using Orchard.Recipes.Services;
|
||||
using Orchard.Settings;
|
||||
|
||||
namespace Orchard.Recipes.RecipeHandlers {
|
||||
public class SettingsRecipeHandler : IRecipeHandler {
|
||||
private readonly ISiteService _siteService;
|
||||
|
||||
public SettingsRecipeHandler(ISiteService siteService) {
|
||||
_siteService = siteService;
|
||||
Logger = NullLogger.Instance;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
ILogger Logger { get; set; }
|
||||
|
||||
/*
|
||||
<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;
|
||||
}
|
||||
|
||||
private static void SetSetting(XAttribute attribute, ContentPart contentPart) {
|
||||
var attributeName = attribute.Name.LocalName;
|
||||
var attributeValue = attribute.Value;
|
||||
var property = contentPart.GetType().GetProperty(attributeName);
|
||||
if (property == null) {
|
||||
throw new InvalidOperationException(string.Format("Could set setting {0} for part {1} because it was not found.", attributeName, contentPart.PartDefinition.Name));
|
||||
}
|
||||
var propertyType = property.PropertyType;
|
||||
if (propertyType == typeof(string)) {
|
||||
property.SetValue(contentPart, attributeValue, null);
|
||||
}
|
||||
else if (propertyType == typeof(bool)) {
|
||||
property.SetValue(contentPart, Boolean.Parse(attributeValue), null);
|
||||
}
|
||||
else if (propertyType == typeof(int)) {
|
||||
property.SetValue(contentPart, Int32.Parse(attributeValue), null);
|
||||
}
|
||||
else {
|
||||
throw new InvalidOperationException(string.Format("Could set setting {0} for part {1} because its type is not supported. Settings should be integer,boolean or string.", attributeName, contentPart.PartDefinition.Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,113 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Web.Hosting;
|
||||
using Orchard.Environment.Extensions;
|
||||
using Orchard.Environment.Extensions.Models;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Packaging.Models;
|
||||
using Orchard.Packaging.Services;
|
||||
using Orchard.Recipes.Models;
|
||||
using Orchard.Recipes.Services;
|
||||
using Orchard.Themes.Services;
|
||||
|
||||
namespace Orchard.Recipes.RecipeHandlers {
|
||||
public class ThemeRecipeHandler : IRecipeHandler {
|
||||
private readonly IPackagingSourceManager _packagingSourceManager;
|
||||
private readonly IPackageManager _packageManager;
|
||||
private readonly IExtensionManager _extensionManager;
|
||||
private readonly IThemeService _themeService;
|
||||
private readonly ISiteThemeService _siteThemeService;
|
||||
|
||||
public ThemeRecipeHandler(
|
||||
IPackagingSourceManager packagingSourceManager,
|
||||
IPackageManager packageManager,
|
||||
IExtensionManager extensionManager,
|
||||
IThemeService themeService,
|
||||
ISiteThemeService siteThemeService) {
|
||||
_packagingSourceManager = packagingSourceManager;
|
||||
_packageManager = packageManager;
|
||||
_extensionManager = extensionManager;
|
||||
_themeService = themeService;
|
||||
_siteThemeService = siteThemeService;
|
||||
Logger = NullLogger.Instance;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
ILogger Logger { get; set; }
|
||||
|
||||
// <Theme name="theme1" repository="somethemerepo" version="1.1" enable="true" current="true" />
|
||||
// install themes from feed.
|
||||
public void ExecuteRecipeStep(RecipeContext recipeContext) {
|
||||
if (!String.Equals(recipeContext.RecipeStep.Name, "Theme", StringComparison.OrdinalIgnoreCase)) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool enable = false, current = false;
|
||||
string name = null, version = null, repository = null;
|
||||
|
||||
foreach (var attribute in recipeContext.RecipeStep.Step.Attributes()) {
|
||||
if (String.Equals(attribute.Name.LocalName, "enable", StringComparison.OrdinalIgnoreCase)) {
|
||||
enable = Boolean.Parse(attribute.Value);
|
||||
}
|
||||
else if (String.Equals(attribute.Name.LocalName, "current", StringComparison.OrdinalIgnoreCase)) {
|
||||
current = Boolean.Parse(attribute.Value);
|
||||
}
|
||||
else if (String.Equals(attribute.Name.LocalName, "name", StringComparison.OrdinalIgnoreCase)) {
|
||||
name = attribute.Value;
|
||||
}
|
||||
else if (String.Equals(attribute.Name.LocalName, "version", StringComparison.OrdinalIgnoreCase)) {
|
||||
version = attribute.Value;
|
||||
}
|
||||
else if (String.Equals(attribute.Name.LocalName, "repository", StringComparison.OrdinalIgnoreCase)) {
|
||||
repository = attribute.Value;
|
||||
}
|
||||
else {
|
||||
Logger.Error("Unrecognized attribute {0} encountered in step Theme. Skipping.", attribute.Name.LocalName);
|
||||
}
|
||||
}
|
||||
|
||||
if (name == null) {
|
||||
throw new InvalidOperationException("Name is required in a Theme declaration in a recipe file.");
|
||||
}
|
||||
// download and install theme from the orchard feed or a custom feed if repository is specified.
|
||||
bool enforceVersion = version != null;
|
||||
bool installed = false;
|
||||
PackagingSource packagingSource = _packagingSourceManager.GetSources().FirstOrDefault();
|
||||
if (repository != null) {
|
||||
enforceVersion = false;
|
||||
packagingSource = new PackagingSource { FeedTitle = repository, FeedUrl = repository };
|
||||
}
|
||||
foreach (var packagingEntry in _packagingSourceManager.GetExtensionList(packagingSource)) {
|
||||
if (String.Equals(packagingEntry.Title, name, StringComparison.OrdinalIgnoreCase)) {
|
||||
if (enforceVersion && !String.Equals(packagingEntry.Version, version, StringComparison.OrdinalIgnoreCase)) {
|
||||
continue;
|
||||
}
|
||||
if (_extensionManager.AvailableExtensions().Where(extension =>
|
||||
DefaultExtensionTypes.IsTheme(extension.ExtensionType) &&
|
||||
String.Equals(packagingEntry.Title, extension.Name, StringComparison.OrdinalIgnoreCase)).Any()) {
|
||||
throw new InvalidOperationException(string.Format("Theme {0} already exists.", name));
|
||||
}
|
||||
_packageManager.Install(packagingEntry.PackageId, packagingEntry.Version, packagingSource.FeedUrl, HostingEnvironment.MapPath("~/"));
|
||||
if (current) {
|
||||
_themeService.EnableThemeFeatures(packagingEntry.Title);
|
||||
_siteThemeService.SetSiteTheme(packagingEntry.Title);
|
||||
}
|
||||
else if (enable) {
|
||||
_themeService.EnableThemeFeatures(packagingEntry.Title);
|
||||
}
|
||||
|
||||
installed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!installed) {
|
||||
throw new InvalidOperationException(string.Format("Theme {0} was not found in the specified location.", name));
|
||||
}
|
||||
|
||||
recipeContext.Executed = true;
|
||||
}
|
||||
}
|
||||
}
|
32
src/Orchard.Web/Modules/Orchard.Recipes/Routes.cs
Normal file
32
src/Orchard.Web/Modules/Orchard.Recipes/Routes.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using Orchard.Mvc.Routes;
|
||||
|
||||
namespace Orchard.Recipes {
|
||||
public class Routes : IRouteProvider {
|
||||
public void GetRoutes(ICollection<RouteDescriptor> routes) {
|
||||
foreach (var routeDescriptor in GetRoutes())
|
||||
routes.Add(routeDescriptor);
|
||||
}
|
||||
|
||||
public IEnumerable<RouteDescriptor> GetRoutes() {
|
||||
return new[] {
|
||||
new RouteDescriptor { Priority = 5,
|
||||
Route = new Route(
|
||||
"Recipes/Status/{executionId}",
|
||||
new RouteValueDictionary {
|
||||
{"area", "Orchard.Recipes"},
|
||||
{"controller", "Recipes"},
|
||||
{"action", "RecipeExecutionStatus"}
|
||||
},
|
||||
new RouteValueDictionary(),
|
||||
new RouteValueDictionary {
|
||||
{"area", "Orchard.Recipes"}
|
||||
},
|
||||
new MvcRouteHandler())
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Orchard.Environment.Extensions;
|
||||
using Orchard.FileSystems.WebSite;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Recipes.Models;
|
||||
|
||||
namespace Orchard.Recipes.Services {
|
||||
public class RecipeHarvester : IRecipeHarvester {
|
||||
private readonly IExtensionManager _extensionManager;
|
||||
private readonly IWebSiteFolder _webSiteFolder;
|
||||
private readonly IRecipeParser _recipeParser;
|
||||
|
||||
public RecipeHarvester(
|
||||
IExtensionManager extensionManager,
|
||||
IWebSiteFolder webSiteFolder,
|
||||
IRecipeParser recipeParser) {
|
||||
_extensionManager = extensionManager;
|
||||
_webSiteFolder = webSiteFolder;
|
||||
_recipeParser = recipeParser;
|
||||
|
||||
Logger = NullLogger.Instance;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
ILogger Logger { get; set; }
|
||||
|
||||
public IEnumerable<Recipe> HarvestRecipes(string extensionName) {
|
||||
var recipes = new List<Recipe>();
|
||||
var extension = _extensionManager.GetExtension(extensionName);
|
||||
if (extension != null) {
|
||||
var recipeLocation = Path.Combine(extension.Location, extensionName, "Recipes");
|
||||
var recipeFiles = _webSiteFolder.ListFiles(recipeLocation, true);
|
||||
recipes.AddRange(
|
||||
from recipeFile in recipeFiles
|
||||
where recipeFile.EndsWith(".recipe.xml", StringComparison.OrdinalIgnoreCase)
|
||||
select _recipeParser.ParseRecipe(_webSiteFolder.ReadFile(recipeFile)));
|
||||
}
|
||||
else {
|
||||
Logger.Error("Could not discover recipes because module '{0}' was not found.", extensionName);
|
||||
}
|
||||
|
||||
return recipes;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Xml.Linq;
|
||||
using Orchard.FileSystems.Media;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Recipes.Models;
|
||||
|
||||
namespace Orchard.Recipes.Services {
|
||||
public class RecipeJournalManager : IRecipeJournal {
|
||||
private readonly IStorageProvider _storageProvider;
|
||||
private readonly string _recipeJournalFolder = "RecipeJournal" + Path.DirectorySeparatorChar;
|
||||
|
||||
public RecipeJournalManager(IStorageProvider storageProvider) {
|
||||
_storageProvider = storageProvider;
|
||||
|
||||
Logger = NullLogger.Instance;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
ILogger Logger { get; set; }
|
||||
|
||||
public void ExecutionStart(string executionId) {
|
||||
var executionJournal = GetJournalFile(executionId);
|
||||
var xElement = XElement.Parse(ReadJournal(executionJournal));
|
||||
xElement.Element("Status").Value = "Started";
|
||||
WriteJournal(executionJournal, xElement);
|
||||
}
|
||||
|
||||
public void ExecutionComplete(string executionId) {
|
||||
var executionJournal = GetJournalFile(executionId);
|
||||
var xElement = XElement.Parse(ReadJournal(executionJournal));
|
||||
xElement.Element("Status").Value = "Complete";
|
||||
WriteJournal(executionJournal, xElement);
|
||||
}
|
||||
|
||||
public void ExecutionFailed(string executionId) {
|
||||
var executionJournal = GetJournalFile(executionId);
|
||||
var xElement = XElement.Parse(ReadJournal(executionJournal));
|
||||
xElement.Element("Status").Value = "Failed";
|
||||
WriteJournal(executionJournal, xElement);
|
||||
}
|
||||
|
||||
public void WriteJournalEntry(string executionId, string message) {
|
||||
var executionJournal = GetJournalFile(executionId);
|
||||
var xElement = XElement.Parse(ReadJournal(executionJournal));
|
||||
var journalEntry = new XElement("Message", message);
|
||||
xElement.Add(journalEntry);
|
||||
WriteJournal(executionJournal, xElement);
|
||||
}
|
||||
|
||||
public RecipeJournal GetRecipeJournal(string executionId) {
|
||||
var executionJournal = GetJournalFile(executionId);
|
||||
var xElement = XElement.Parse(ReadJournal(executionJournal));
|
||||
|
||||
var journal = new RecipeJournal { ExecutionId = executionId };
|
||||
var messages = new List<JournalMessage>();
|
||||
|
||||
journal.Status = ReadStatusFromJournal(xElement);
|
||||
foreach (var message in xElement.Elements("Message")) {
|
||||
messages.Add(new JournalMessage {Message = message.Value});
|
||||
}
|
||||
journal.Messages = messages;
|
||||
|
||||
return journal;
|
||||
}
|
||||
|
||||
public RecipeStatus GetRecipeStatus(string executionId) {
|
||||
var executionJournal = GetJournalFile(executionId);
|
||||
var xElement = XElement.Parse(ReadJournal(executionJournal));
|
||||
|
||||
return ReadStatusFromJournal(xElement);
|
||||
}
|
||||
|
||||
private IStorageFile GetJournalFile(string executionId) {
|
||||
IStorageFile journalFile;
|
||||
var journalPath = Path.Combine(_recipeJournalFolder, executionId);
|
||||
try {
|
||||
_storageProvider.TryCreateFolder(_recipeJournalFolder);
|
||||
journalFile = _storageProvider.GetFile(journalPath);
|
||||
}
|
||||
catch (ArgumentException) {
|
||||
journalFile = _storageProvider.CreateFile(journalPath);
|
||||
var recipeStepElement = new XElement("RecipeJournal");
|
||||
recipeStepElement.Add(new XElement("Status", "Unknown"));
|
||||
WriteJournal(journalFile, recipeStepElement);
|
||||
}
|
||||
|
||||
return journalFile;
|
||||
}
|
||||
|
||||
private static string ReadJournal(IStorageFile executionJournal) {
|
||||
using (var stream = executionJournal.OpenRead()) {
|
||||
using (var streamReader = new StreamReader(stream)) {
|
||||
return streamReader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteJournal(IStorageFile journalFile, XElement journal) {
|
||||
string content = journal.ToString();
|
||||
using (var stream = journalFile.OpenWrite()) {
|
||||
using (var tw = new StreamWriter(stream)) {
|
||||
tw.Write(content);
|
||||
stream.SetLength(stream.Position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static RecipeStatus ReadStatusFromJournal(XElement xElement) {
|
||||
switch (xElement.Element("Status").Value) {
|
||||
case "Started":
|
||||
return RecipeStatus.Started;
|
||||
case "Complete":
|
||||
return RecipeStatus.Complete;
|
||||
case "Failed":
|
||||
return RecipeStatus.Failed;
|
||||
default:
|
||||
return RecipeStatus.Unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Recipes.Models;
|
||||
|
||||
namespace Orchard.Recipes.Services {
|
||||
public class RecipeManager : IRecipeManager {
|
||||
private readonly IRecipeStepQueue _recipeStepQueue;
|
||||
private readonly IRecipeScheduler _recipeScheduler;
|
||||
private readonly IRecipeJournal _recipeJournal;
|
||||
|
||||
public RecipeManager(IRecipeStepQueue recipeStepQueue, IRecipeScheduler recipeScheduler, IRecipeJournal recipeJournal) {
|
||||
_recipeStepQueue = recipeStepQueue;
|
||||
_recipeScheduler = recipeScheduler;
|
||||
_recipeJournal = recipeJournal;
|
||||
|
||||
Logger = NullLogger.Instance;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
ILogger Logger { get; set; }
|
||||
|
||||
public string Execute(Recipe recipe) {
|
||||
if (recipe == null)
|
||||
return null;
|
||||
|
||||
var executionId = Guid.NewGuid().ToString("n");
|
||||
_recipeJournal.ExecutionStart(executionId);
|
||||
|
||||
foreach (var recipeStep in recipe.RecipeSteps) {
|
||||
_recipeStepQueue.Enqueue(executionId, recipeStep);
|
||||
}
|
||||
_recipeScheduler.ScheduleWork(executionId);
|
||||
|
||||
return executionId;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Xml.Linq;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Recipes.Models;
|
||||
|
||||
namespace Orchard.Recipes.Services {
|
||||
public class RecipeParser : IRecipeParser {
|
||||
public RecipeParser() {
|
||||
Logger = NullLogger.Instance;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
ILogger Logger { get; set; }
|
||||
|
||||
public Recipe ParseRecipe(string recipeText) {
|
||||
var recipe = new Recipe();
|
||||
|
||||
try {
|
||||
var recipeSteps = new List<RecipeStep>();
|
||||
TextReader textReader = new StringReader(recipeText);
|
||||
var recipeTree = XElement.Load(textReader, LoadOptions.PreserveWhitespace);
|
||||
textReader.Close();
|
||||
|
||||
foreach (var element in recipeTree.Elements()) {
|
||||
// Recipe mETaDaTA
|
||||
if (element.Name.LocalName == "Recipe") {
|
||||
foreach (var metadataElement in element.Elements()) {
|
||||
switch (metadataElement.Name.LocalName) {
|
||||
case "Name":
|
||||
recipe.Name = metadataElement.Value;
|
||||
break;
|
||||
case "Description":
|
||||
recipe.Description = metadataElement.Value;
|
||||
break;
|
||||
case "Author":
|
||||
recipe.Author = metadataElement.Value;
|
||||
break;
|
||||
case "WebSite":
|
||||
recipe.WebSite = metadataElement.Value;
|
||||
break;
|
||||
case "Version":
|
||||
recipe.Version = metadataElement.Value;
|
||||
break;
|
||||
case "Tags":
|
||||
recipe.Tags = metadataElement.Value;
|
||||
break;
|
||||
default:
|
||||
Logger.Error("Unrecognized recipe metadata element {0} encountered. Skipping.", metadataElement.Name.LocalName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Recipe step
|
||||
else {
|
||||
var recipeStep = new RecipeStep { Name = element.Name.LocalName, Step = element };
|
||||
recipeSteps.Add(recipeStep);
|
||||
}
|
||||
}
|
||||
recipe.RecipeSteps = recipeSteps;
|
||||
}
|
||||
catch (Exception exception) {
|
||||
Logger.Error(exception, "Parsing recipe failed. Recipe text was: {0}.", recipeText);
|
||||
throw;
|
||||
}
|
||||
|
||||
return recipe;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Environment.Configuration;
|
||||
using Orchard.Environment.Descriptor;
|
||||
using Orchard.Environment.State;
|
||||
using Orchard.Recipes.Events;
|
||||
|
||||
namespace Orchard.Recipes.Services {
|
||||
public class RecipeScheduler : IRecipeScheduler, IRecipeSchedulerEventHandler {
|
||||
private readonly IProcessingEngine _processingEngine;
|
||||
private readonly ShellSettings _shellSettings;
|
||||
private readonly IShellDescriptorManager _shellDescriptorManager;
|
||||
private readonly Lazy<IRecipeStepExecutor> _recipeStepExecutor;
|
||||
|
||||
public RecipeScheduler(
|
||||
IProcessingEngine processingEngine,
|
||||
ShellSettings shellSettings,
|
||||
IShellDescriptorManager shellDescriptorManager,
|
||||
Lazy<IRecipeStepExecutor> recipeStepExecutor) {
|
||||
_processingEngine = processingEngine;
|
||||
_shellSettings = shellSettings;
|
||||
_shellDescriptorManager = shellDescriptorManager;
|
||||
_recipeStepExecutor = recipeStepExecutor;
|
||||
}
|
||||
|
||||
public void ScheduleWork(string executionId) {
|
||||
var shellDescriptor = _shellDescriptorManager.GetShellDescriptor();
|
||||
// TODO: this task entry may need to become appdata folder backed if it isn't already
|
||||
_processingEngine.AddTask(
|
||||
_shellSettings,
|
||||
shellDescriptor,
|
||||
"IRecipeSchedulerEventHandler.ExecuteWork",
|
||||
new Dictionary<string, object> { { "executionId", executionId } });
|
||||
}
|
||||
|
||||
public void ExecuteWork(string executionId) {
|
||||
// todo: this callback should be guarded against concurrency by the IProcessingEngine
|
||||
var scheduleMore = _recipeStepExecutor.Value.ExecuteNextStep(executionId);
|
||||
if (scheduleMore)
|
||||
ScheduleWork(executionId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Recipes.Models;
|
||||
|
||||
namespace Orchard.Recipes.Services {
|
||||
public class RecipeStepExecutor : IRecipeStepExecutor {
|
||||
private readonly IRecipeStepQueue _recipeStepQueue;
|
||||
private readonly IRecipeJournal _recipeJournal;
|
||||
private readonly IEnumerable<IRecipeHandler> _recipeHandlers;
|
||||
|
||||
public RecipeStepExecutor(IRecipeStepQueue recipeStepQueue, IRecipeJournal recipeJournal, IEnumerable<IRecipeHandler> recipeHandlers) {
|
||||
_recipeStepQueue = recipeStepQueue;
|
||||
_recipeJournal = recipeJournal;
|
||||
_recipeHandlers = recipeHandlers;
|
||||
|
||||
Logger = NullLogger.Instance;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
ILogger Logger { get; set; }
|
||||
|
||||
public bool ExecuteNextStep(string executionId) {
|
||||
var nextRecipeStep= _recipeStepQueue.Dequeue(executionId);
|
||||
if (nextRecipeStep == null) {
|
||||
_recipeJournal.ExecutionComplete(executionId);
|
||||
return false;
|
||||
}
|
||||
_recipeJournal.WriteJournalEntry(executionId, string.Format("Executing step {0}.", nextRecipeStep.Name));
|
||||
var recipeContext = new RecipeContext { RecipeStep = nextRecipeStep, Executed = false };
|
||||
try {
|
||||
foreach (var recipeHandler in _recipeHandlers) {
|
||||
recipeHandler.ExecuteRecipeStep(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);
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Orchard.FileSystems.AppData;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Recipes.Models;
|
||||
|
||||
namespace Orchard.Recipes.Services {
|
||||
public class RecipeStepQueue : IRecipeStepQueue {
|
||||
private readonly IAppDataFolder _appDataFolder;
|
||||
private readonly string _recipeQueueFolder = "RecipeQueue" + Path.DirectorySeparatorChar;
|
||||
|
||||
public RecipeStepQueue(IAppDataFolder appDataFolder) {
|
||||
_appDataFolder = appDataFolder;
|
||||
Logger = NullLogger.Instance;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
ILogger Logger { get; set; }
|
||||
|
||||
public void Enqueue(string executionId, RecipeStep step) {
|
||||
var recipeStepElement = new XElement("RecipeStep");
|
||||
recipeStepElement.Add(new XElement("Name", step.Name));
|
||||
recipeStepElement.Add(step.Step);
|
||||
|
||||
if (_appDataFolder.DirectoryExists(Path.Combine(_recipeQueueFolder, executionId))) {
|
||||
int stepIndex = GetLastStepIndex(executionId) + 1;
|
||||
_appDataFolder.CreateFile(Path.Combine(_recipeQueueFolder, executionId + Path.DirectorySeparatorChar + stepIndex),
|
||||
recipeStepElement.ToString());
|
||||
}
|
||||
else {
|
||||
_appDataFolder.CreateFile(
|
||||
Path.Combine(_recipeQueueFolder, executionId + Path.DirectorySeparatorChar + "0"),
|
||||
recipeStepElement.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public RecipeStep Dequeue(string executionId) {
|
||||
if (!_appDataFolder.DirectoryExists(Path.Combine(_recipeQueueFolder, executionId))) {
|
||||
return null;
|
||||
}
|
||||
RecipeStep recipeStep = null;
|
||||
int stepIndex = GetFirstStepIndex(executionId);
|
||||
if (stepIndex >= 0) {
|
||||
var stepPath = Path.Combine(_recipeQueueFolder, executionId + Path.DirectorySeparatorChar + stepIndex);
|
||||
// string to xelement
|
||||
var stepElement = XElement.Parse(_appDataFolder.ReadFile(stepPath));
|
||||
var stepName = stepElement.Element("Name").Value;
|
||||
recipeStep = new RecipeStep {
|
||||
Name = stepName,
|
||||
Step = stepElement.Element(stepName)
|
||||
};
|
||||
_appDataFolder.DeleteFile(stepPath);
|
||||
}
|
||||
|
||||
if (stepIndex < 1) {
|
||||
_appDataFolder.DeleteFile(Path.Combine(_recipeQueueFolder, executionId));
|
||||
}
|
||||
|
||||
return recipeStep;
|
||||
}
|
||||
|
||||
private int GetFirstStepIndex(string executionId) {
|
||||
var stepFiles = new List<string>(_appDataFolder.ListFiles(Path.Combine(_recipeQueueFolder, executionId)));
|
||||
if (stepFiles.Count == 0)
|
||||
return -1;
|
||||
var currentSteps = stepFiles.Select(stepFile => Int32.Parse(stepFile.Substring(stepFile.LastIndexOf('/') + 1))).ToList();
|
||||
currentSteps.Sort();
|
||||
return currentSteps[0];
|
||||
}
|
||||
|
||||
private int GetLastStepIndex(string executionId) {
|
||||
int lastIndex = -1;
|
||||
var stepFiles = _appDataFolder.ListFiles(Path.Combine(_recipeQueueFolder, executionId));
|
||||
// we always have only a handful of steps.
|
||||
foreach (var stepFile in stepFiles) {
|
||||
int stepOrder = Int32.Parse(stepFile.Substring(stepFile.LastIndexOf('/') + 1));
|
||||
if (stepOrder > lastIndex)
|
||||
lastIndex = stepOrder;
|
||||
}
|
||||
|
||||
return lastIndex;
|
||||
}
|
||||
}
|
||||
}
|
39
src/Orchard.Web/Modules/Orchard.Recipes/web.config
Normal file
39
src/Orchard.Web/Modules/Orchard.Recipes/web.config
Normal file
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0"?>
|
||||
<configuration>
|
||||
|
||||
<configSections>
|
||||
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
|
||||
<remove name="host" />
|
||||
<remove name="pages" />
|
||||
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
|
||||
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
|
||||
</sectionGroup>
|
||||
</configSections>
|
||||
|
||||
<system.web.webPages.razor>
|
||||
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||
<pages pageBaseType="Orchard.Mvc.ViewEngines.Razor.WebViewPage">
|
||||
<namespaces>
|
||||
<add namespace="System.Web.Mvc" />
|
||||
<add namespace="System.Web.Mvc.Ajax" />
|
||||
<add namespace="System.Web.Mvc.Html" />
|
||||
<add namespace="System.Web.Routing" />
|
||||
<add namespace="System.Linq"/>
|
||||
<add namespace="System.Collections.Generic"/>
|
||||
<add namespace="Orchard.Mvc.Html"/>
|
||||
</namespaces>
|
||||
</pages>
|
||||
</system.web.webPages.razor>
|
||||
|
||||
<system.web>
|
||||
<compilation targetFramework="4.0">
|
||||
<assemblies>
|
||||
<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add assembly="System.Data.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
|
||||
<add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||
</assemblies>
|
||||
</compilation>
|
||||
</system.web>
|
||||
|
||||
</configuration>
|
@@ -1,8 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.Environment;
|
||||
using Orchard.Environment.Configuration;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Recipes.Models;
|
||||
using Orchard.Setup.Services;
|
||||
using Orchard.Setup.ViewModels;
|
||||
using Orchard.Localization;
|
||||
@@ -16,8 +19,12 @@ namespace Orchard.Setup.Controllers {
|
||||
private readonly IViewsBackgroundCompilation _viewsBackgroundCompilation;
|
||||
private readonly INotifier _notifier;
|
||||
private readonly ISetupService _setupService;
|
||||
private const string DefaultRecipe = "Default";
|
||||
|
||||
public SetupController(INotifier notifier, ISetupService setupService, IViewsBackgroundCompilation viewsBackgroundCompilation) {
|
||||
public SetupController(
|
||||
INotifier notifier,
|
||||
ISetupService setupService,
|
||||
IViewsBackgroundCompilation viewsBackgroundCompilation) {
|
||||
_viewsBackgroundCompilation = viewsBackgroundCompilation;
|
||||
_notifier = notifier;
|
||||
_setupService = setupService;
|
||||
@@ -35,18 +42,24 @@ namespace Orchard.Setup.Controllers {
|
||||
|
||||
public ActionResult Index() {
|
||||
var initialSettings = _setupService.Prime();
|
||||
|
||||
var recipes = _setupService.Recipes().Where(r => r.Name != DefaultRecipe);
|
||||
|
||||
// On the first time installation of Orchard, the user gets to the setup screen, which
|
||||
// will take a while to finish (user inputting data and the setup process itself).
|
||||
// We use this opportunity to start a background task to "pre-compile" all the known
|
||||
// views in the app folder, so that the application is more reponsive when the user
|
||||
// hits the homepage and admin screens for the first time.
|
||||
// hits the homepage and admin screens for the first time.))
|
||||
if (StringComparer.OrdinalIgnoreCase.Equals(initialSettings.Name, ShellSettings.DefaultName)) {
|
||||
_viewsBackgroundCompilation.Start();
|
||||
}
|
||||
|
||||
//
|
||||
return IndexViewResult(new SetupViewModel { AdminUsername = "admin", DatabaseIsPreconfigured = !string.IsNullOrEmpty(initialSettings.DataProvider)});
|
||||
|
||||
return IndexViewResult(new SetupViewModel {
|
||||
AdminUsername = "admin",
|
||||
DatabaseIsPreconfigured = !string.IsNullOrEmpty(initialSettings.DataProvider),
|
||||
Recipes = recipes
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost, ActionName("Index")]
|
||||
@@ -65,14 +78,21 @@ namespace Orchard.Setup.Controllers {
|
||||
ModelState.AddModelError("DatabaseTablePrefix", T("The table prefix must begin with a letter").Text);
|
||||
}
|
||||
}
|
||||
|
||||
if (model.Recipe == null) {
|
||||
model.Recipe = DefaultRecipe;
|
||||
}
|
||||
if (!ModelState.IsValid) {
|
||||
var recipes = _setupService.Recipes().Where(r => r.Name != DefaultRecipe);
|
||||
model.Recipes = recipes;
|
||||
foreach (var recipe in recipes.Where(recipe => recipe.Name == model.Recipe)) {
|
||||
model.RecipeDescription = recipe.Description;
|
||||
}
|
||||
model.DatabaseIsPreconfigured = !string.IsNullOrEmpty(_setupService.Prime().DataProvider);
|
||||
|
||||
return IndexViewResult(model);
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
var setupContext = new SetupContext {
|
||||
SiteName = model.SiteName,
|
||||
AdminUsername = model.AdminUsername,
|
||||
@@ -80,10 +100,11 @@ namespace Orchard.Setup.Controllers {
|
||||
DatabaseProvider = model.DatabaseOptions ? "SqlCe" : "SqlServer",
|
||||
DatabaseConnectionString = model.DatabaseConnectionString,
|
||||
DatabaseTablePrefix = model.DatabaseTablePrefix,
|
||||
EnabledFeatures = null // default list
|
||||
EnabledFeatures = null, // default list
|
||||
Recipe = model.Recipe
|
||||
};
|
||||
|
||||
_setupService.Setup(setupContext);
|
||||
string executionId = _setupService.Setup(setupContext);
|
||||
|
||||
// First time installation if finally done. Tell the background views compilation
|
||||
// process to stop, so that it doesn't interfere with the user (asp.net compilation
|
||||
|
@@ -9,3 +9,4 @@ Features:
|
||||
Name: Setup
|
||||
Description: Standard site setup. This feature is disabled automatically once setup is over.
|
||||
Category: Core
|
||||
|
@@ -76,6 +76,10 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Content\synchronizing.gif" />
|
||||
<Content Include="Recipes\blog.recipe.xml" />
|
||||
<Content Include="Recipes\default.recipe.xml" />
|
||||
<Content Include="Recipes\customcontentsite.recipe.xml" />
|
||||
<Content Include="Recipes\minimal.recipe.xml" />
|
||||
<Content Include="Scripts\setup.js" />
|
||||
<Content Include="Views\Setup\Index.cshtml" />
|
||||
</ItemGroup>
|
||||
@@ -88,14 +92,14 @@
|
||||
<Project>{9916839C-39FC-4CEB-A5AF-89CA7E87119F}</Project>
|
||||
<Name>Orchard.Core</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard.Recipes\Orchard.Recipes.csproj">
|
||||
<Project>{FC1D74E8-7A4D-48F4-83DE-95C6173780C4}</Project>
|
||||
<Name>Orchard.Recipes</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard.Themes\Orchard.Themes.csproj">
|
||||
<Project>{CDE24A24-01D3-403C-84B9-37722E18DFB7}</Project>
|
||||
<Name>Orchard.Themes</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard.Widgets\Orchard.Widgets.csproj">
|
||||
<Project>{194D3CCC-1153-474D-8176-FDE8D7D0D0BD}</Project>
|
||||
<Name>Orchard.Widgets</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Module.txt" />
|
||||
|
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0"?>
|
||||
<Orchard>
|
||||
<Recipe>
|
||||
<Name>Blog</Name>
|
||||
<Description>A recipe providing an installation profile with the features you need for a personal blog.</Description>
|
||||
<Author>The Orchard Team</Author>
|
||||
<WebSite>http://orchardproject.net</WebSite>
|
||||
<Tags>blog</Tags>
|
||||
<Version>1.0</Version>
|
||||
</Recipe>
|
||||
|
||||
<Feature enable="Orchard.Blogs,Orchard.Comments,Orchard.Tags,
|
||||
XmlRpc,Orchard.Blogs.RemotePublishing,
|
||||
TinyMce,Orchard.Media,Orchard.PublishLater,
|
||||
Orchard.jQuery,Orchard.Widgets,
|
||||
Orchard.Scripting,Orchard.Scripting.Lightweight,
|
||||
PackagingServices,Orchard.Packaging,Gallery,
|
||||
TheThemeMachine" />
|
||||
|
||||
<Metadata>
|
||||
<Types>
|
||||
<Page ContentTypeSettings.Draftable="True" TypeIndexing.Included="true">
|
||||
<TagsPart />
|
||||
<LocalizationPart />
|
||||
</Page>
|
||||
<BlogPost ContentTypeSettings.Draftable="True" TypeIndexing.Included="true">
|
||||
<CommentsPart />
|
||||
<TagsPart />
|
||||
<LocalizationPart />
|
||||
</BlogPost>
|
||||
</Types>
|
||||
<Parts>
|
||||
<BodyPart BodyPartSettings.FlavorDefault="html" />
|
||||
</Parts>
|
||||
</Metadata>
|
||||
|
||||
<Settings>
|
||||
<SiteSettingsPart PageSize="20" PageTitleSeparator = " - " />
|
||||
<CommentSettingsPart ModerateComments="true" />
|
||||
</Settings>
|
||||
|
||||
<Command>
|
||||
layer create /Name:"Default" /LayerRule:"true"
|
||||
layer create /Name:"Authenticated" /LayerRule:"authenticated"
|
||||
layer create /Name:"Anonymous" /LayerRule:"not authenticated"
|
||||
layer create /Name:"Disabled" /LayerRule:"false"
|
||||
layer create /Name:"TheHomepage" /LayerRule:"url '~/'"
|
||||
blog create /Slug:"blog" /Title:"Blog" /Homepage:true /Description:"This is your Orchard Blog."
|
||||
widget create /Type:"HtmlWidget" /Title:"First Leader Aside" /Zone:"TripelFirst" /Position:"5" /Layer:"TheHomepage" /UseLoremIpsumText:true
|
||||
widget create /Type:"HtmlWidget" /Title:"Second Leader Aside" /Zone:"TripelSecond" /Position:"5" /Layer:"TheHomepage" /UseLoremIpsumText:true
|
||||
widget create /Type:"HtmlWidget" /Title:"Third Leader Aside" /Zone:"TripelThird" /Position:"5" /Layer:"TheHomepage" /UseLoremIpsumText:true
|
||||
menuitem create /MenuPosition:"1" /MenuText:"Home" /Url:"" /OnMainMenu:true
|
||||
</Command>
|
||||
|
||||
<Migration features="*" />
|
||||
</Orchard>
|
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0"?>
|
||||
<Orchard>
|
||||
<Recipe>
|
||||
<Name>Custom Content Site</Name>
|
||||
<Description>A recipe providing an installation profile with the functionality needed to create content-based custom sites.</Description>
|
||||
<Author>The Orchard Team</Author>
|
||||
<WebSite>http://orchardproject.net</WebSite>
|
||||
<Tags></Tags>
|
||||
<Version>1.0</Version>
|
||||
</Recipe>
|
||||
|
||||
<Feature enable="Orchard.ContentTypes,Orchard.Lists,Orchard.PublishLater,
|
||||
Orchard.jQuery,TinyMce,Orchard.Media,
|
||||
PackagingServices,Orchard.Packaging,Gallery,TheThemeMachine" />
|
||||
|
||||
<Metadata>
|
||||
<Types>
|
||||
<Page ContentTypeSettings.Draftable="True" TypeIndexing.Included="true" />
|
||||
</Types>
|
||||
<Parts>
|
||||
<BodyPart BodyPartSettings.FlavorDefault="html" />
|
||||
<!-- Dynamic parts -->
|
||||
</Parts>
|
||||
</Metadata>
|
||||
|
||||
<Settings>
|
||||
</Settings>
|
||||
|
||||
<Command>
|
||||
page create /Slug:"welcome-to-orchard" /Title:"Welcome to Orchard!" /Path:"welcome-to-orchard" /Homepage:true /Publish:true /UseWelcomeText:true
|
||||
menuitem create /MenuPosition:"1" /MenuText:"Home" /Url:"" /OnMainMenu:true
|
||||
</Command>
|
||||
|
||||
<Migration features="*" />
|
||||
</Orchard>
|
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0"?>
|
||||
<Orchard>
|
||||
<Recipe>
|
||||
<Name>Default</Name>
|
||||
<Description>A recipe providing a default Orchard installation profile.</Description>
|
||||
<Author>The Orchard Team</Author>
|
||||
<WebSite>http://orchardproject.net</WebSite>
|
||||
<Tags></Tags>
|
||||
<Version>1.0</Version>
|
||||
</Recipe>
|
||||
|
||||
<Feature enable="Orchard.Blogs,Orchard.Comments,Orchard.Tags,
|
||||
Orchard.Lists,TinyMce,Orchard.Media,Orchard.PublishLater,
|
||||
Orchard.jQuery,Orchard.Widgets,Orchard.ContentTypes,
|
||||
Orchard.Scripting,Orchard.Scripting.Lightweight,
|
||||
PackagingServices,Orchard.Packaging,Gallery,
|
||||
TheThemeMachine" />
|
||||
|
||||
<Metadata>
|
||||
<Types>
|
||||
<Page ContentTypeSettings.Draftable="True" TypeIndexing.Included="true">
|
||||
<TagsPart />
|
||||
<LocalizationPart />
|
||||
</Page>
|
||||
<BlogPost ContentTypeSettings.Draftable="True" TypeIndexing.Included="true">
|
||||
<CommentsPart />
|
||||
<TagsPart />
|
||||
<LocalizationPart />
|
||||
</BlogPost>
|
||||
</Types>
|
||||
<Parts>
|
||||
<BodyPart BodyPartSettings.FlavorDefault="html" />
|
||||
</Parts>
|
||||
</Metadata>
|
||||
|
||||
<Settings />
|
||||
|
||||
<Command>
|
||||
layer create /Name:"Default" /LayerRule:"true"
|
||||
layer create /Name:"Authenticated" /LayerRule:"authenticated"
|
||||
layer create /Name:"Anonymous" /LayerRule:"not authenticated"
|
||||
layer create /Name:"Disabled" /LayerRule:"false"
|
||||
layer create /Name:"TheHomepage" /LayerRule:"url '~/'"
|
||||
page create /Slug:"welcome-to-orchard" /Title:"Welcome to Orchard!" /Path:"welcome-to-orchard" /Homepage:true /Publish:true /UseWelcomeText:true
|
||||
widget create /Type:"HtmlWidget" /Title:"First Leader Aside" /Zone:"TripelFirst" /Position:"5" /Layer:"TheHomepage" /UseLoremIpsumText:true
|
||||
widget create /Type:"HtmlWidget" /Title:"Second Leader Aside" /Zone:"TripelSecond" /Position:"5" /Layer:"TheHomepage" /UseLoremIpsumText:true
|
||||
widget create /Type:"HtmlWidget" /Title:"Third Leader Aside" /Zone:"TripelThird" /Position:"5" /Layer:"TheHomepage" /UseLoremIpsumText:true
|
||||
menuitem create /MenuPosition:"1" /MenuText:"Home" /Url:"" /OnMainMenu:true
|
||||
</Command>
|
||||
|
||||
<Migration features="*" />
|
||||
</Orchard>
|
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0"?>
|
||||
<Orchard>
|
||||
<Recipe>
|
||||
<Name>Minimal</Name>
|
||||
<Description>A recipe providing the Orchard framework with limited end-user functionality that can be used during development.</Description>
|
||||
<Author>The Orchard Team</Author>
|
||||
<WebSite>http://orchardproject.net</WebSite>
|
||||
<Tags>developer</Tags>
|
||||
<Version>1.0</Version>
|
||||
</Recipe>
|
||||
|
||||
<Feature disable="Feeds, Containers"
|
||||
enable="Orchard.jQuery" />
|
||||
|
||||
<Metadata>
|
||||
<Types>
|
||||
<Page ContentTypeSettings.Draftable="True" />
|
||||
</Types>
|
||||
<Parts>
|
||||
<BodyPart BodyPartSettings.FlavorDefault="html" />
|
||||
</Parts>
|
||||
</Metadata>
|
||||
|
||||
<Settings />
|
||||
|
||||
<Command>
|
||||
page create /Slug:"welcome-to-orchard" /Title:"Welcome to Orchard!" /Path:"welcome-to-orchard" /Homepage:true /Publish:true /Text:"Welcome To Orchard!"
|
||||
menuitem create /MenuPosition:"1" /MenuText:"Home" /Url:"" /OnMainMenu:true
|
||||
</Command>
|
||||
|
||||
<Migration features="*" />
|
||||
</Orchard>
|
@@ -12,3 +12,12 @@
|
||||
document.forms[0].attachEvent("onsubmit", show);
|
||||
}
|
||||
})();
|
||||
|
||||
(function ($) {
|
||||
$("select.recipe").change(function () { // class="recipe" on the select element
|
||||
var description = $(this).find(":selected").attr("recipedescription"); // reads the html attribute of the selected option
|
||||
$("#recipedescription").text(description); // make the contents of <div id="recipe-description"></div> be the escaped description string
|
||||
});
|
||||
})(jQuery);
|
||||
|
||||
|
||||
|
@@ -1,8 +1,11 @@
|
||||
using Orchard.Environment.Configuration;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Environment.Configuration;
|
||||
using Orchard.Recipes.Models;
|
||||
|
||||
namespace Orchard.Setup.Services {
|
||||
public interface ISetupService : IDependency {
|
||||
ShellSettings Prime();
|
||||
void Setup(SetupContext context);
|
||||
IEnumerable<Recipe> Recipes();
|
||||
string Setup(SetupContext context);
|
||||
}
|
||||
}
|
@@ -9,5 +9,6 @@ namespace Orchard.Setup.Services {
|
||||
public string DatabaseConnectionString { get; set; }
|
||||
public string DatabaseTablePrefix { get; set; }
|
||||
public IEnumerable<string> EnabledFeatures { get; set; }
|
||||
public string Recipe { get; set; }
|
||||
}
|
||||
}
|
@@ -1,14 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Web;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Core.Common.Settings;
|
||||
using Orchard.Core.Contents.Extensions;
|
||||
using Orchard.Core.Navigation.Models;
|
||||
using Orchard.Core.Routable.Models;
|
||||
using Orchard.Core.Settings.Descriptor.Records;
|
||||
using Orchard.Core.Settings.Models;
|
||||
using Orchard.Data;
|
||||
@@ -16,13 +11,13 @@ using Orchard.Data.Migration.Interpreters;
|
||||
using Orchard.Data.Migration.Schema;
|
||||
using Orchard.Environment;
|
||||
using Orchard.Environment.Configuration;
|
||||
using Orchard.Environment.Extensions;
|
||||
using Orchard.Environment.ShellBuilders;
|
||||
using Orchard.Environment.Descriptor;
|
||||
using Orchard.Environment.Descriptor.Models;
|
||||
using Orchard.Indexing;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Localization.Services;
|
||||
using Orchard.Recipes.Models;
|
||||
using Orchard.Recipes.Services;
|
||||
using Orchard.Reports.Services;
|
||||
using Orchard.Security;
|
||||
using Orchard.Settings;
|
||||
@@ -30,8 +25,6 @@ using Orchard.Environment.State;
|
||||
using Orchard.Data.Migration;
|
||||
using Orchard.Themes.Services;
|
||||
using Orchard.Utility.Extensions;
|
||||
using Orchard.Widgets.Models;
|
||||
using Orchard.Widgets;
|
||||
|
||||
namespace Orchard.Setup.Services {
|
||||
public class SetupService : ISetupService {
|
||||
@@ -41,6 +34,8 @@ namespace Orchard.Setup.Services {
|
||||
private readonly IShellContainerFactory _shellContainerFactory;
|
||||
private readonly ICompositionStrategy _compositionStrategy;
|
||||
private readonly IProcessingEngine _processingEngine;
|
||||
private readonly IRecipeHarvester _recipeHarvester;
|
||||
private readonly IEnumerable<Recipe> _recipes;
|
||||
|
||||
public SetupService(
|
||||
ShellSettings shellSettings,
|
||||
@@ -48,13 +43,16 @@ namespace Orchard.Setup.Services {
|
||||
IShellSettingsManager shellSettingsManager,
|
||||
IShellContainerFactory shellContainerFactory,
|
||||
ICompositionStrategy compositionStrategy,
|
||||
IProcessingEngine processingEngine) {
|
||||
IProcessingEngine processingEngine,
|
||||
IRecipeHarvester recipeHarvester) {
|
||||
_shellSettings = shellSettings;
|
||||
_orchardHost = orchardHost;
|
||||
_shellSettingsManager = shellSettingsManager;
|
||||
_shellContainerFactory = shellContainerFactory;
|
||||
_compositionStrategy = compositionStrategy;
|
||||
_processingEngine = processingEngine;
|
||||
_recipeHarvester = recipeHarvester;
|
||||
_recipes = _recipeHarvester.HarvestRecipes("Orchard.Setup");
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
@@ -64,54 +62,23 @@ namespace Orchard.Setup.Services {
|
||||
return _shellSettings;
|
||||
}
|
||||
|
||||
public void Setup(SetupContext context) {
|
||||
public IEnumerable<Recipe> Recipes() {
|
||||
return _recipes;
|
||||
}
|
||||
|
||||
public string Setup(SetupContext context) {
|
||||
string executionId = null;
|
||||
// The vanilla Orchard distibution has the following features enabled.
|
||||
if (context.EnabledFeatures == null || context.EnabledFeatures.Count() == 0) {
|
||||
string[] hardcoded = {
|
||||
// Framework
|
||||
"Orchard.Framework",
|
||||
|
||||
// Core
|
||||
"Common",
|
||||
"Containers",
|
||||
"Contents",
|
||||
"Dashboard",
|
||||
"Feeds",
|
||||
"HomePage",
|
||||
"Navigation",
|
||||
"Reports",
|
||||
"Routable",
|
||||
"Scheduling",
|
||||
"Settings",
|
||||
"Shapes",
|
||||
|
||||
// Other
|
||||
"Orchard.PublishLater", // todo: (sebros) remove
|
||||
"Orchard.Blogs",
|
||||
"Orchard.Comments",
|
||||
"Orchard.ContentTypes",
|
||||
"Orchard.jQuery",
|
||||
"Orchard.Lists",
|
||||
"Orchard.Media",
|
||||
"Common", "Containers", "Contents", "Dashboard", "Feeds", "HomePage", "Navigation", "Reports", "Routable", "Scheduling", "Settings", "Shapes",
|
||||
// Modules
|
||||
"Orchard.Pages", "Orchard.Themes", "Orchard.Users", "Orchard.Roles", "Orchard.Modules",
|
||||
"PackagingServices", "Orchard.Packaging", "Gallery", "Orchard.Recipes"
|
||||
"Orchard.MediaPicker",
|
||||
"Orchard.Modules",
|
||||
"Orchard.Pages",
|
||||
"Orchard.Roles",
|
||||
"Orchard.Tags",
|
||||
"Orchard.Themes",
|
||||
"Orchard.Users",
|
||||
"Orchard.Scripting",
|
||||
"Orchard.Scripting.Lightweight",
|
||||
"Orchard.Widgets",
|
||||
"TinyMce",
|
||||
|
||||
// Gallery/Packaging
|
||||
"PackagingServices",
|
||||
"Orchard.Packaging",
|
||||
"Gallery",
|
||||
|
||||
// Themes
|
||||
"TheThemeMachine",
|
||||
};
|
||||
|
||||
context.EnabledFeatures = hardcoded;
|
||||
@@ -168,7 +135,7 @@ namespace Orchard.Setup.Services {
|
||||
var dataMigrationManager = environment.Resolve<IDataMigrationManager>();
|
||||
dataMigrationManager.Update("Settings");
|
||||
|
||||
foreach ( var feature in context.EnabledFeatures ) {
|
||||
foreach (var feature in context.EnabledFeatures) {
|
||||
dataMigrationManager.Update(feature);
|
||||
}
|
||||
|
||||
@@ -183,7 +150,6 @@ namespace Orchard.Setup.Services {
|
||||
while ( _processingEngine.AreTasksPending() )
|
||||
_processingEngine.ExecuteNextTask();
|
||||
|
||||
|
||||
// creating a standalone environment.
|
||||
// in theory this environment can be used to resolve any normal components by interface, and those
|
||||
// components will exist entirely in isolation - no crossover between the safemode container currently in effect
|
||||
@@ -192,7 +158,7 @@ namespace Orchard.Setup.Services {
|
||||
shellSettings.State = new TenantState("Running");
|
||||
using (var environment = _orchardHost.CreateStandaloneEnvironment(shellSettings)) {
|
||||
try {
|
||||
CreateTenantData(context, environment);
|
||||
executionId = CreateTenantData(context, environment);
|
||||
}
|
||||
catch {
|
||||
environment.Resolve<ITransactionManager>().Cancel();
|
||||
@@ -201,9 +167,12 @@ namespace Orchard.Setup.Services {
|
||||
}
|
||||
|
||||
_shellSettingsManager.SaveSettings(shellSettings);
|
||||
|
||||
return executionId;
|
||||
}
|
||||
|
||||
private void CreateTenantData(SetupContext context, IWorkContextScope environment) {
|
||||
private string CreateTenantData(SetupContext context, IWorkContextScope environment) {
|
||||
string executionId = null;
|
||||
// create superuser
|
||||
var membershipService = environment.Resolve<IMembershipService>();
|
||||
var user =
|
||||
@@ -221,7 +190,6 @@ namespace Orchard.Setup.Services {
|
||||
siteSettings.Record.SiteSalt = Guid.NewGuid().ToString("N");
|
||||
siteSettings.Record.SiteName = context.SiteName;
|
||||
siteSettings.Record.SuperUser = context.AdminUsername;
|
||||
siteSettings.Record.PageTitleSeparator = " - ";
|
||||
siteSettings.Record.SiteCulture = "en-US";
|
||||
|
||||
// set site theme
|
||||
@@ -232,110 +200,15 @@ namespace Orchard.Setup.Services {
|
||||
var cultureManager = environment.Resolve<ICultureManager>();
|
||||
cultureManager.AddCulture("en-US");
|
||||
|
||||
var contentManager = environment.Resolve<IContentManager>();
|
||||
|
||||
// this needs to exit the standalone environment? rework this process entirely?
|
||||
// simulate installation-time module activation events
|
||||
//var hackInstallationGenerator = environment.Resolve<IHackInstallationGenerator>();
|
||||
//hackInstallationGenerator.GenerateInstallEvents();
|
||||
|
||||
var contentDefinitionManager = environment.Resolve<IContentDefinitionManager>();
|
||||
//todo: (heskew) pull these definitions (and initial content creation) out into a distribution configuration when we have that capability
|
||||
contentDefinitionManager.AlterTypeDefinition("BlogPost", cfg => cfg
|
||||
.WithPart("CommentsPart")
|
||||
.WithPart("TagsPart")
|
||||
.WithPart("LocalizationPart")
|
||||
.Draftable()
|
||||
.Indexed()
|
||||
);
|
||||
contentDefinitionManager.AlterTypeDefinition("Page", cfg => cfg
|
||||
.WithPart("TagsPart")
|
||||
.WithPart("LocalizationPart")
|
||||
.Draftable()
|
||||
.Indexed()
|
||||
);
|
||||
contentDefinitionManager.AlterPartDefinition("BodyPart", cfg => cfg
|
||||
.WithSetting("BodyPartSettings.FlavorDefault", BodyPartSettings.FlavorDefaultDefault));
|
||||
|
||||
// If "Orchard.Widgets" is enabled, setup default layers and widgets
|
||||
var extensionManager = environment.Resolve<IExtensionManager>();
|
||||
var shellDescriptor = environment.Resolve<ShellDescriptor>();
|
||||
if (extensionManager.EnabledFeatures(shellDescriptor).Where(d => d.Id == "Orchard.Widgets").Any()) {
|
||||
// Create default layers
|
||||
var layerInitializer = environment.Resolve<IDefaultLayersInitializer>();
|
||||
layerInitializer.CreateDefaultLayers();
|
||||
|
||||
// add a layer for the homepage
|
||||
var homepageLayer = contentManager.Create("Layer", VersionOptions.Draft);
|
||||
homepageLayer.As<LayerPart>().Name = "TheHomepage";
|
||||
homepageLayer.As<LayerPart>().LayerRule = "url \"~/\"";
|
||||
contentManager.Publish(homepageLayer);
|
||||
|
||||
// and three more for the tripel...really need this elsewhere...
|
||||
var tripelFirst = contentManager.Create("HtmlWidget", VersionOptions.Draft);
|
||||
tripelFirst.As<WidgetPart>().LayerPart = homepageLayer.As<LayerPart>();
|
||||
tripelFirst.As<WidgetPart>().Title = T("First Leader Aside").Text;
|
||||
tripelFirst.As<WidgetPart>().Zone = "TripelFirst";
|
||||
tripelFirst.As<WidgetPart>().Position = "5";
|
||||
tripelFirst.As<BodyPart>().Text = "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur a nibh ut tortor dapibus vestibulum. Aliquam vel sem nibh. Suspendisse vel condimentum tellus.</p>";
|
||||
contentManager.Publish(tripelFirst);
|
||||
|
||||
var tripelSecond = contentManager.Create("HtmlWidget", VersionOptions.Draft);
|
||||
tripelSecond.As<WidgetPart>().LayerPart = homepageLayer.As<LayerPart>();
|
||||
tripelSecond.As<WidgetPart>().Title = T("Second Leader Aside").Text;
|
||||
tripelSecond.As<WidgetPart>().Zone = "TripelSecond";
|
||||
tripelSecond.As<WidgetPart>().Position = "5";
|
||||
tripelSecond.As<BodyPart>().Text = "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur a nibh ut tortor dapibus vestibulum. Aliquam vel sem nibh. Suspendisse vel condimentum tellus.</p>";
|
||||
contentManager.Publish(tripelSecond);
|
||||
|
||||
var tripelThird = contentManager.Create("HtmlWidget", VersionOptions.Draft);
|
||||
tripelThird.As<WidgetPart>().LayerPart = homepageLayer.As<LayerPart>();
|
||||
tripelThird.As<WidgetPart>().Title = T("Third Leader Aside").Text;
|
||||
tripelThird.As<WidgetPart>().Zone = "TripelThird";
|
||||
tripelThird.As<WidgetPart>().Position = "5";
|
||||
tripelThird.As<BodyPart>().Text = "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur a nibh ut tortor dapibus vestibulum. Aliquam vel sem nibh. Suspendisse vel condimentum tellus.</p>";
|
||||
contentManager.Publish(tripelThird);
|
||||
}
|
||||
|
||||
// create a welcome page that's promoted to the home page
|
||||
var page = contentManager.Create("Page", VersionOptions.Draft);
|
||||
page.As<RoutePart>().Title = T("Welcome to Orchard!").Text;
|
||||
page.As<RoutePart>().Path = "welcome-to-orchard";
|
||||
page.As<RoutePart>().Slug = "welcome-to-orchard";
|
||||
page.As<RoutePart>().PromoteToHomePage = true;
|
||||
page.As<BodyPart>().Text = T(
|
||||
@"<p>You've successfully setup your Orchard Site and this is the homepage of your new site.
|
||||
Here are a few things you can look at to get familiar with the application.
|
||||
Once you feel confident you don't need this anymore, you can
|
||||
<a href=""Admin/Contents/Edit/{0}"">remove it by going into editing mode</a>
|
||||
and replacing it with whatever you want.</p>
|
||||
<p>First things first - You'll probably want to <a href=""Admin/Settings"">manage your settings</a>
|
||||
and configure Orchard to your liking. After that, you can head over to
|
||||
<a href=""Admin/Themes"">manage themes to change or install new themes</a>
|
||||
and really make it your own. Once you're happy with a look and feel, it's time for some content.
|
||||
You can start creating new custom content types or start from the built-in ones by
|
||||
<a href=""Admin/Contents/Create/Page"">adding a page</a>, <a href=""Admin/Blogs/Create"">creating a blog</a>
|
||||
or <a href=""Admin/Navigation"">managing your menus.</a></p>
|
||||
<p>Finally, Orchard has been designed to be extended. It comes with a few built-in
|
||||
modules such as pages and blogs or themes. If you're looking to add additional functionality,
|
||||
you can do so by creating your own module or by installing one that somebody else built.
|
||||
Modules are created by other users of Orchard just like you so if you feel up to it,
|
||||
<a href=""http://orchardproject.net/contribution"">please consider participating</a>.</p>
|
||||
<p>Thanks for using Orchard – The Orchard Team </p>", page.Id).Text;
|
||||
|
||||
contentManager.Publish(page);
|
||||
|
||||
// add a menu item for the shiny new home page
|
||||
var menuItem = contentManager.Create("MenuItem");
|
||||
menuItem.As<MenuPart>().MenuPosition = "1";
|
||||
menuItem.As<MenuPart>().MenuText = T("Home").ToString();
|
||||
menuItem.As<MenuPart>().OnMainMenu = true;
|
||||
menuItem.As<MenuItemPart>().Url = "";
|
||||
var recipeManager = environment.Resolve<IRecipeManager>();
|
||||
executionId = recipeManager.Execute(Recipes().Where(r => r.Name == context.Recipe).FirstOrDefault());
|
||||
|
||||
//null check: temporary fix for running setup in command line
|
||||
if (HttpContext.Current != null) {
|
||||
authenticationService.SignIn(user, true);
|
||||
}
|
||||
|
||||
return executionId;
|
||||
}
|
||||
}
|
||||
}
|
@@ -27,6 +27,7 @@ using Orchard.Mvc.ViewEngines;
|
||||
using Orchard.Mvc.ViewEngines.Razor;
|
||||
using Orchard.Mvc.ViewEngines.ThemeAwareness;
|
||||
using Orchard.Mvc.ViewEngines.WebForms;
|
||||
using Orchard.Recipes.Services;
|
||||
using Orchard.Settings;
|
||||
using Orchard.Themes;
|
||||
using Orchard.UI.Notify;
|
||||
@@ -75,6 +76,9 @@ namespace Orchard.Setup {
|
||||
builder.RegisterType<DefaultDataMigrationInterpreter>().As<IDataMigrationInterpreter>().InstancePerLifetimeScope();
|
||||
builder.RegisterType<DataMigrationManager>().As<IDataMigrationManager>().InstancePerLifetimeScope();
|
||||
|
||||
builder.RegisterType<RecipeHarvester>().As<IRecipeHarvester>().InstancePerLifetimeScope();
|
||||
builder.RegisterType<RecipeParser>().As<IRecipeParser>().InstancePerLifetimeScope();
|
||||
|
||||
// in progress - adding services for display/shape support in setup
|
||||
builder.RegisterType<DisplayHelperFactory>().As<IDisplayHelperFactory>();
|
||||
builder.RegisterType<DefaultDisplayManager>().As<IDisplayManager>();
|
||||
|
@@ -1,3 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Recipes.Models;
|
||||
using Orchard.Setup.Annotations;
|
||||
|
||||
namespace Orchard.Setup.ViewModels {
|
||||
@@ -19,5 +21,9 @@ namespace Orchard.Setup.ViewModels {
|
||||
public string DatabaseConnectionString { get; set; }
|
||||
public string DatabaseTablePrefix { get; set; }
|
||||
public bool DatabaseIsPreconfigured { get; set; }
|
||||
|
||||
public IEnumerable<Recipe> Recipes { get; set; }
|
||||
public string Recipe { get; set; }
|
||||
public string RecipeDescription { get; set; }
|
||||
}
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
@model Orchard.Setup.ViewModels.SetupViewModel
|
||||
@{
|
||||
Script.Require("jquery").AtFoot();
|
||||
Script.Include("setup.js").AtFoot();
|
||||
}
|
||||
<h1>@Html.TitleForPage(T("Get Started").ToString())</h1>
|
||||
@@ -50,11 +51,25 @@ if (!Model.DatabaseIsPreconfigured) {
|
||||
</div>
|
||||
</fieldset>
|
||||
}
|
||||
<fieldset>
|
||||
<legend>@T("Choose an Orchard Recipe (Optional)")</legend>
|
||||
<div>@T("Orchard Recipes allow you to setup your site with additional pre-configured options, features and settings out of the box")</div>
|
||||
<div>
|
||||
<select id="@Html.FieldIdFor(m => m.Recipe)" name="@Html.FieldNameFor(m => m.Recipe)" class="recipe">
|
||||
@Html.SelectOption("", false, T("Choose").ToString(), new { recipedescription = "" })
|
||||
@Html.SelectOption("", false, T("-------------------").ToString(), new { recipedescription = "" })
|
||||
@foreach(var recipe in Model.Recipes) {
|
||||
@Html.SelectOption(Model.Recipe, recipe.Name, recipe.Name, new { recipedescription = recipe.Description })
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div id="recipedescription">@Model.RecipeDescription</div>
|
||||
</fieldset>
|
||||
<div id="throbber">
|
||||
<div class="curtain"></div>
|
||||
<div class="curtain-content">
|
||||
<div>
|
||||
<h1>@T("Configuring Orchard...")</h1>
|
||||
<h1>@T("Cooking Orchard Recipe ...")</h1>
|
||||
<p>
|
||||
<img src="@Href("../../content/synchronizing.gif")" alt="" />
|
||||
</p>
|
||||
|
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using Orchard.Commands;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Aspects;
|
||||
using Orchard.Security;
|
||||
using Orchard.Settings;
|
||||
using Orchard.Widgets.Models;
|
||||
|
||||
namespace Orchard.Widgets.Commands {
|
||||
public class LayerCommands : DefaultOrchardCommandHandler {
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly ISiteService _siteService;
|
||||
private readonly IMembershipService _membershipService;
|
||||
|
||||
public LayerCommands(IContentManager contentManager, ISiteService siteService, IMembershipService membershipService) {
|
||||
_contentManager = contentManager;
|
||||
_siteService = siteService;
|
||||
_membershipService = membershipService;
|
||||
}
|
||||
|
||||
[OrchardSwitch]
|
||||
public string Name { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public string LayerRule { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public string Description { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public string Owner { get; set; }
|
||||
|
||||
[CommandName("layer create")]
|
||||
[CommandHelp("layer create /Name:<name> /LayerRule:<rule> [/Description:<description>] [/Owner:<owner>]\r\n\t" + "Creates a new layer")]
|
||||
[OrchardSwitches("Name,LayerRule,Description,Owner")]
|
||||
public void Create() {
|
||||
IContent layer = _contentManager.Create<LayerPart>("Layer", t => {
|
||||
t.Record.Name = Name;
|
||||
t.Record.LayerRule = LayerRule;
|
||||
t.Record.Description = Description ?? String.Empty;
|
||||
});
|
||||
|
||||
_contentManager.Publish(layer.ContentItem);
|
||||
if (String.IsNullOrEmpty(Owner)) {
|
||||
Owner = _siteService.GetSiteSettings().SuperUser;
|
||||
}
|
||||
var owner = _membershipService.GetUser(Owner);
|
||||
layer.As<ICommonPart>().Owner = owner;
|
||||
|
||||
Context.Output.WriteLine(T("Layer created successfully.").Text);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Orchard.Commands;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Aspects;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Security;
|
||||
using Orchard.Settings;
|
||||
using Orchard.Widgets.Models;
|
||||
using Orchard.Widgets.Services;
|
||||
|
||||
namespace Orchard.Widgets.Commands {
|
||||
public class WidgetCommands : DefaultOrchardCommandHandler {
|
||||
private readonly IWidgetsService _widgetsService;
|
||||
private readonly ISiteService _siteService;
|
||||
private readonly IMembershipService _membershipService;
|
||||
private const string LoremIpsum = "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur a nibh ut tortor dapibus vestibulum. Aliquam vel sem nibh. Suspendisse vel condimentum tellus.</p>";
|
||||
|
||||
public WidgetCommands(IWidgetsService widgetsService, ISiteService siteService, IMembershipService membershipService) {
|
||||
_widgetsService = widgetsService;
|
||||
_siteService = siteService;
|
||||
_membershipService = membershipService;
|
||||
}
|
||||
|
||||
[OrchardSwitch]
|
||||
public string Type { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public string Title { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public string Zone { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public string Position { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public string Layer { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public string Owner { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public string Text { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public bool UseLoremIpsumText { get; set; }
|
||||
|
||||
[OrchardSwitch]
|
||||
public bool Publish { get; set; }
|
||||
|
||||
[CommandName("widget create")]
|
||||
[CommandHelp("widget create /Type:<type> /Title:<title> /Zone:<zone> /Position:<position> /Layer:<layer> [/Owner:<owner>] [/Text:<text>] [/UseLoremIpsumText:true|false]\r\n\t" + "Creates a new html widget")]
|
||||
[OrchardSwitches("Type,Title,Zone,Position,Layer,Owner,Text,UseLoremIpsumText")]
|
||||
public void Create() {
|
||||
var layer = GetLayer(Layer);
|
||||
if (layer == null) {
|
||||
throw new OrchardException(T("Creating widget failed : layer {0} was not found.", Layer));
|
||||
}
|
||||
|
||||
var widget = _widgetsService.CreateWidget(layer.ContentItem.Id, Type, T(Title).Text, Position, Zone);
|
||||
var text = String.Empty;
|
||||
if (UseLoremIpsumText) {
|
||||
text = T(LoremIpsum).Text;
|
||||
}
|
||||
else {
|
||||
if (!String.IsNullOrEmpty(Text)) {
|
||||
text = Text;
|
||||
}
|
||||
}
|
||||
widget.As<BodyPart>().Text = text;
|
||||
if (String.IsNullOrEmpty(Owner)) {
|
||||
Owner = _siteService.GetSiteSettings().SuperUser;
|
||||
}
|
||||
var owner = _membershipService.GetUser(Owner);
|
||||
widget.As<ICommonPart>().Owner = owner;
|
||||
|
||||
Context.Output.WriteLine(T("Widget created successfully.").Text);
|
||||
}
|
||||
|
||||
private LayerPart GetLayer(string layer) {
|
||||
var layers = _widgetsService.GetLayers();
|
||||
return layers.FirstOrDefault(layerPart => String.Equals(layerPart.Name, layer, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
}
|
@@ -45,6 +45,8 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AdminMenu.cs" />
|
||||
<Compile Include="Commands\LayerCommands.cs" />
|
||||
<Compile Include="Commands\WidgetCommands.cs" />
|
||||
<Compile Include="Controllers\AdminController.cs" />
|
||||
<Compile Include="Drivers\LayerPartDriver.cs" />
|
||||
<Compile Include="Drivers\WidgetPartDriver.cs" />
|
||||
|
@@ -128,6 +128,12 @@ input[type="password"] {
|
||||
width:98%;
|
||||
}
|
||||
|
||||
select {
|
||||
padding:3px;
|
||||
border:1px solid #bdbcbc;
|
||||
width:100%;
|
||||
}
|
||||
|
||||
button.primaryAction, .button.primaryAction, .button.primaryAction:link, .button.primaryAction:visited {
|
||||
background:#4687ad;
|
||||
border:1px solid #405f71;
|
||||
|
Reference in New Issue
Block a user