--HG--
branch : dev
This commit is contained in:
Sebastien Ros
2010-05-14 17:07:00 -07:00
16 changed files with 254 additions and 149 deletions

View File

@@ -0,0 +1,32 @@
using Autofac;
using NUnit.Framework;
using Orchard.Caching;
namespace Orchard.Tests.Caching {
[TestFixture]
public class CacheTests {
private IContainer _container;
private ICacheManager _cacheManager;
[SetUp]
public void Init() {
var builder = new ContainerBuilder();
builder.RegisterType<DefaultCacheManager>().As<ICacheManager>();
_container = builder.Build();
_cacheManager = _container.Resolve<ICacheManager>();
}
[Test]
public void CacheManagerShouldReturnCacheItem() {
var result = _cacheManager.Get("testItem", ctx => "testResult");
Assert.That(result, Is.EqualTo("testResult"));
}
[Test]
public void CacheManagerShouldReturnExistingCacheItem() {
_cacheManager.Get("testItem", ctx => "testResult");
var result = _cacheManager.Get("testItem", ctx => "");
Assert.That(result, Is.EqualTo("testResult"));
}
}
}

View File

@@ -117,6 +117,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Caching\CacheTests.cs" />
<Compile Include="Commands\CommandHandlerDescriptorBuilderTests.cs" />
<Compile Include="Commands\CommandHandlerTests.cs" />
<Compile Include="Commands\CommandManagerTests.cs" />

View File

@@ -44,44 +44,12 @@ namespace Orchard.Modules.Controllers {
});
}
public ActionResult Features(FeaturesOptions options) {
public ActionResult Features() {
if (!Services.Authorizer.Authorize(Permissions.ManageFeatures, T("Not allowed to manage features")))
return new HttpUnauthorizedResult();
var features = _moduleService.GetAvailableFeatures();
return View(new FeaturesViewModel {Features = features, Options = options});
}
[HttpPost, ActionName("Features")]
[FormValueRequired("submit.BulkEdit")]
public ActionResult FeaturesPOST(FeaturesOptions options, IList<string> selection) {
if (selection != null && selection.Count > 0)
{
switch (options.BulkAction)
{
case FeaturesBulkAction.None:
break;
case FeaturesBulkAction.Enable:
if (!Services.Authorizer.Authorize(Permissions.ManageFeatures, T("Not allowed to enable features")))
return new HttpUnauthorizedResult();
_moduleService.EnableFeatures(selection);
//todo: (heskew) need better messages
//todo: (heskew) hmmm...need a helper to comma-separate all but last, which would get the " and " treatment...all localized, of course
Services.Notifier.Information(T("{0} were enabled", string.Join(", ", selection.ToArray())));
break;
case FeaturesBulkAction.Disable:
if (!Services.Authorizer.Authorize(Permissions.ManageFeatures, T("Not allowed to disable features")))
return new HttpUnauthorizedResult();
_moduleService.DisableFeatures(selection);
//todo: (heskew) need better messages
Services.Notifier.Information(T("{0} were disabled", string.Join(", ", selection.ToArray())));
break;
default:
throw new ArgumentOutOfRangeException();
}
}
return RedirectToAction("Features");
return View(new FeaturesViewModel {Features = features});
}
[ValidateAntiForgeryTokenOrchard]
@@ -107,7 +75,7 @@ namespace Orchard.Modules.Controllers {
return new NotFoundResult();
_moduleService.DisableFeatures(new[] { featureName });
Services.Notifier.Information(T("{0} was disabled", featureName));
//Services.Notifier.Information(T("{0} was disabled", featureName));
return RedirectToAction("Features");
}

View File

@@ -48,10 +48,11 @@ namespace Orchard.Modules.Services {
}
public IEnumerable<IModuleFeature> GetAvailableFeatures() {
var enabledFeatures = _shellDescriptorManager.GetShellDescriptor().EnabledFeatures;
var enabledFeatures = _shellDescriptorManager.GetShellDescriptor().EnabledFeatures.ToList();
return GetInstalledModules()
.SelectMany(m => _extensionManager.LoadFeatures(m.Features))
.Select(f => AssembleModuleFromDescriptor(f, enabledFeatures.FirstOrDefault(sf => string.Equals(sf.Name, f.Descriptor.Name, StringComparison.OrdinalIgnoreCase)) != null));
.Select(f => AssembleModuleFromDescriptor(f, enabledFeatures.FirstOrDefault(sf => string.Equals(sf.Name, f.Descriptor.Name, StringComparison.OrdinalIgnoreCase)) != null))
.ToList();
}
public IEnumerable<Feature> GetAvailableFeaturesByModule(string moduleName) {
@@ -69,9 +70,20 @@ namespace Orchard.Modules.Services {
public void DisableFeatures(IEnumerable<string> featureNames) {
var shellDescriptor = _shellDescriptorManager.GetShellDescriptor();
var enabledFeatures = shellDescriptor.EnabledFeatures.ToList();
enabledFeatures.RemoveAll(f => featureNames.Contains(f.Name));
var features = GetAvailableFeatures();
foreach (var featureName in featureNames) {
var feature = featureName;
var dependants = features.Where(f => f.IsEnabled && f.Descriptor.Dependencies != null && f.Descriptor.Dependencies.Contains(feature));
if (dependants.Count() == 0) {
enabledFeatures.RemoveAll(f => f.Name == feature);
}
else {
// list what else will be disabled with ok/cancel
}
}
_shellDescriptorManager.UpdateShellDescriptor(shellDescriptor.SerialNumber, enabledFeatures, shellDescriptor.Parameters);
}

View File

@@ -4,16 +4,5 @@ using Orchard.Mvc.ViewModels;
namespace Orchard.Modules.ViewModels {
public class FeaturesViewModel : BaseViewModel {
public IEnumerable<IModuleFeature> Features { get; set; }
public FeaturesOptions Options { get; set; }
}
public class FeaturesOptions {
public FeaturesBulkAction BulkAction { get; set; }
}
public enum FeaturesBulkAction {
None,
Enable,
Disable
}
}

View File

@@ -2,31 +2,18 @@
<%@ Import Namespace="Orchard.Mvc.Html"%>
<%@ Import Namespace="Orchard.Modules.ViewModels"%>
<h1><%=Html.TitleForPage(T("Manage Features").ToString()) %></h1>
<div class="manage" style="visibility:hidden"><%=Html.ActionLink(T("∞").ToString(), "Features", new { }, new { @class = "button primaryAction" })%></div>
<% if (Model.Features.Count() > 0) {
using (Html.BeginFormAntiForgeryPost()) { %>
<%=Html.ValidationSummary()%>
<fieldset class="actions bulk">
<label for="publishActions"><%=_Encoded("Actions: ")%></label>
<select id="publishActions" name="<%=Html.NameOf(m => m.Options.BulkAction) %>">
<%=Html.SelectOption(Model.Options.BulkAction, FeaturesBulkAction.None, _Encoded("Choose action...").ToString())%>
<%=Html.SelectOption(Model.Options.BulkAction, FeaturesBulkAction.Enable, _Encoded("Enable").ToString())%>
<%=Html.SelectOption(Model.Options.BulkAction, FeaturesBulkAction.Disable, _Encoded("Disable").ToString())%>
</select>
<input class="button" type="submit" name="submit.BulkEdit" value="<%=_Encoded("Apply") %>" />
</fieldset>
<fieldset class="pageList">
<% if (Model.Features.Count() > 0) { %>
<ul class="contentItems"><%
foreach (var featureGroup in Model.Features.OrderBy(f => f.Descriptor.Category).GroupBy(f => f.Descriptor.Category)) { %>
<li<%=featureGroup == Model.Features.Last() ? " class=\"last\"" : "" %>>
var featureGroups = Model.Features.OrderBy(f => f.Descriptor.Category).GroupBy(f => f.Descriptor.Category);
foreach (var featureGroup in featureGroups) { %>
<li<%=featureGroup == featureGroups.Last() ? " class=\"last\"" : "" %>>
<h2><%=Html.Encode(featureGroup.First().Descriptor.Category ?? T("Uncategorized")) %></h2>
<ul><%
foreach (var feature in featureGroup.OrderBy(f => f.Descriptor.Name)) {%>
<li<%=feature == featureGroup.Last() ? " class=\"last\"" : "" %> id="<%=Html.Encode(feature.Descriptor.Name) %>">
var features = featureGroup.OrderBy(f => f.Descriptor.Name);
foreach (var feature in features) {%>
<li<%=feature == features.Last() ? " class=\"last\"" : "" %> id="<%=Html.Encode(feature.Descriptor.Name) %>">
<div class="summary">
<div class="properties">
<input type="checkbox" name="selection" value="<%=Html.Encode(feature.Descriptor.Name) %>" />
<h3><%=Html.Encode(feature.Descriptor.Name) %></h3>
<ul class="pageStatus">
<li><%
@@ -45,10 +32,16 @@ using (Html.BeginFormAntiForgeryPost()) { %>
</ul>
</div>
<div class="related"><%
if (feature.IsEnabled) { %>
<a href="<%=Html.AntiForgeryTokenGetUrl(Url.Action("Disable", new { featureName = feature.Descriptor.Name, area = "Orchard.Modules" })) %>"><%=_Encoded("Disable") %></a><%
} else { %>
<a href="<%=Html.AntiForgeryTokenGetUrl(Url.Action("Enable", new { featureName = feature.Descriptor.Name, area = "Orchard.Modules" })) %>"><%=_Encoded("Enable") %></a><%
if (feature.IsEnabled) {
using (Html.BeginFormAntiForgeryPost(string.Format("{0}#{1}", Url.Action("Disable", new { area = "Orchard.Modules" }), Html.AttributeEncode(feature.Descriptor.Name)), FormMethod.Post, new {@class = "inline link"})) { %>
<%=Html.Hidden("featureName", feature.Descriptor.Name) %>
<button type="submit"><%=_Encoded("Disable") %></button><%
}
} else {
using (Html.BeginFormAntiForgeryPost(string.Format("{0}#{1}", Url.Action("Enable", new { area = "Orchard.Modules" }), Html.AttributeEncode(feature.Descriptor.Name)), FormMethod.Post, new {@class = "inline link"})) { %>
<%=Html.Hidden("featureName", feature.Descriptor.Name) %>
<button type="submit"><%=_Encoded("Enable") %></button><%
}
} %>
</div>
</div>
@@ -59,5 +52,3 @@ using (Html.BeginFormAntiForgeryPost()) { %>
} %>
</ul><%
} %>
</fieldset><%
} %>

View File

@@ -50,10 +50,10 @@ jQuery.fn.extend({
(function() {
$("form.inline.link").each(function() {
var _this = $(this);
var link = $("<a href='.'/>");
var link = $("<a class='wasFormInlineLink' href='.'/>");
var button = _this.children("button").first();
link.text(button.text())
.css("cursor", "pointer")
.addClass(button.attr("class"))
.click(function() { _this.submit(); return false; })
.unload(function() { _this = 0; });
_this.replaceWith(link);

View File

@@ -27,33 +27,24 @@
<div>
<h3><%=Html.Encode(theme.DisplayName) %></h3>
<%=Html.Image(Html.ThemePath(theme, "/Theme.png"), Html.Encode(theme.DisplayName), null)%>
<% using (Html.BeginFormAntiForgeryPost(Url.Action("Activate"), FormMethod.Post, new { @class = "inline" }))
{ %>
<fieldset>
<button type="submit" title="<%=_Encoded("Activate") %>" name="themeName" value="<%=theme.ThemeName %>"><%=_Encoded("Activate") %></button>
</fieldset>
<% using (Html.BeginFormAntiForgeryPost(Url.Action("Activate"), FormMethod.Post, new { @class = "inline" })) { %>
<%=Html.Hidden("themeName", theme.ThemeName)%>
<button type="submit" title="<%=_Encoded("Activate") %>"><%=_Encoded("Activate") %></button>
<% } %>
<% using (Html.BeginFormAntiForgeryPost(Url.Action("Preview"), FormMethod.Post, new { @class = "inline" }))
{ %>
<fieldset>
<button type="submit" title="<%=_Encoded("Preview") %>" name="themeName" value="<%=theme.ThemeName %>"><%=_Encoded("Preview") %></button>
</fieldset>
<% using (Html.BeginFormAntiForgeryPost(Url.Action("Preview"), FormMethod.Post, new { @class = "inline" })) { %>
<%=Html.Hidden("themeName", theme.ThemeName)%>
<button type="submit" title="<%=_Encoded("Preview") %>"><%=_Encoded("Preview") %></button>
<% } %>
<h5><%=_Encoded("By") %> <%=Html.Encode(theme.Author) %></h5>
<p>
<%=_Encoded("Version:") %> <%=Html.Encode(theme.Version) %><br />
<%=Html.Encode(theme.Description) %><br />
<%=Html.Encode(theme.HomePage) %>
</p>
<% using (Html.BeginFormAntiForgeryPost(Url.Action("Uninstall"), FormMethod.Post, new { @class = "inline" }))
{ %>
<fieldset>
<button type="submit" class="linkButton" title="<%=_Encoded("Uninstall") %>" name="themeName" value="<%=theme.ThemeName %>"><%=_Encoded("Uninstall")%></button>
</fieldset>
<% using (Html.BeginFormAntiForgeryPost(Url.Action("Uninstall"), FormMethod.Post, new { @class = "inline link" })) { %>
<%=Html.Hidden("themeName", theme.ThemeName)%>
<button type="submit" class="uninstall" title="<%=_Encoded("Uninstall") %>"><%=_Encoded("Uninstall")%></button>
<% } %>
</div>
</li>
<% }

View File

@@ -2,37 +2,68 @@
<%@ Import Namespace="Orchard.Mvc.Html"%>
<%@ Import Namespace="Orchard.Themes.ViewModels"%>
<style type="text/css">
#themepreview, #themepreview fieldset, #themepreview input, #themepreview select
{
margin: 0; padding: 0; border:none; font-size: 1em; width:auto; color: #000;
font-family:Frutiger,"Frutiger Linotype",Univers,Calibri,"Gill Sans","Gill Sans MT","Myriad Pro",Myriad,"DejaVu Sans Condensed","Liberation Sans","Nimbus Sans L",Tahoma,Geneva,"Helvetica Neue",Helvetica,Arial,sans-serif;
body {
margin-top:40px;
}
#themepreview {
background:#2D2F25 url('<%=ResolveUrl("../../Styles/Images/toolBarBackground.gif") %>') repeat-x left top;
border-bottom:1px solid #494d4d;
font-size:15px;
left:0;
height:30px;
margin:0;
position:absolute;
overflow:hidden;
padding:5px 0;
top:0;
width:100%;
}
#themepreview fieldset,
#themepreview span, #themepreview input,
#themepreview select, #themepreview button {
border:none;
color:#000;
font:1em/1em Frutiger,"Frutiger Linotype",Univers,Calibri,"Gill Sans","Gill Sans MT","Myriad Pro",Myriad,"DejaVu Sans Condensed","Liberation Sans","Nimbus Sans L",Tahoma,Geneva,"Helvetica Neue",Helvetica,Arial,sans-serif;
margin:0;
padding:0;
width:auto;
}
/*#themepreview { background: #000; font-size:15px; padding:5px; }*/
#themepreview span { color: #ccc; padding-right:5px; }
#themepreview fieldset { margin: 0; padding: 3px; }
/*#themepreview button { font-size: 13px; padding:0 3px; margin-left:10px; }*/
#themepreview button.preview { margin-left:0; }
#themepreview fieldset { padding:3px 8px; }
html.dyn #themepreview button.preview { display:none; }
#themepreview fieldset * { float:left; }
#themepreview fieldset span { line-height:1.6em; }
#themepreview button.cancel { float:right; }
#themepreview { background: #2D2F25 url('<%=ResolveUrl("../../Styles/Images/toolBarBackground.gif") %>') repeat-x left top; font-size:15px; padding:5px; border-bottom:1px solid #494d4d;}
/* Button styles */
#themepreview button { font-size: 13px; padding:2px 4px; margin: 0 0 0 10px; text-align:center; color:#f1f1f1; border:1px solid; border-top-color:#191d1d; border-right-color:#494d4d; border-bottom-color:#494d4d; border-left-color:#202626; background:#2a2626 url('<%=ResolveUrl("../../Styles/Images/toolBarActiveButtonBackground.gif") %>') repeat-x left center; }
/* Hover for buttons */
#themepreview button:hover { color:#fdcc64; border:1px #545959 solid; cursor:pointer; background:#2a2626 url('<%=ResolveUrl("../../Styles/Images/toolBarHoverButtonBackground.gif") %>') repeat-x left center; }
#themepreview button {
background:#2a2626 url('<%=ResolveUrl("../../Styles/Images/toolBarActiveButtonBackground.gif") %>') repeat-x left center;
border:1px solid;
border-top-color:#191d1d;
border-right-color:#494d4d;
border-bottom-color:#494d4d;
border-left-color:#202626;
color:#f1f1f1;
line-height:1.22em;
margin: 0 0 0 10px;
padding:0 4px 1px;
text-align:center;
}
#themepreview button:hover {
background:#2a2626 url('<%=ResolveUrl("../../Styles/Images/toolBarHoverButtonBackground.gif") %>') repeat-x left center;
border-color:#545959;
color:#fdcc64;
cursor:pointer;
}
</style>
<div id="themepreview">
<% using(Html.BeginFormAntiForgeryPost(Url.Action("Preview", new{Controller="Admin", Area="Orchard.Themes"}), FormMethod.Post, new { @class = "inline" })) { %>
<fieldset>
<span><%=T("You are previewing: ")%></span>
<%=Html.Hidden("ReturnUrl", Context.Request.Url)%>
<%=Html.DropDownList("ThemeName", Model.Themes, new {onChange = "this.form.submit();"})%>
<button type="submit" class="preview" title="<%=_Encoded("Preview")%>" name="submit.Preview" value="<%=_Encoded("Preview")%>"><%=_Encoded("Preview")%></button>
<button type="submit" title="<%=_Encoded("Apply")%>" name="submit.Apply" value="<%=_Encoded("Apply")%>"><%=_Encoded("Apply this theme") %></button>
<button type="submit" class="cancel" title="<%=_Encoded("Cancel")%>" name="submit.Cancel" value="<%=_Encoded("Cancel")%>"><%=_Encoded("Cancel")%></button>
<%=Html.Hidden("ReturnUrl", Context.Request.Url)%>
</fieldset>
<% } %>
</div>

View File

@@ -731,8 +731,11 @@ todo: (heskew) pull out into relevant modules where appropriate
zoom:1;
*display: inline;
}
.templates fieldset {
margin:0 0 .933%;
.templates .inline button {
font-size:1.2em;
}
.templates .wasFormInlineLink {
font-size:1.4em;
}
.templates p {
overflow:hidden;

View File

@@ -0,0 +1,4 @@
namespace Orchard.Caching {
public class AcquireContext {
}
}

View File

@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
namespace Orchard.Caching {
public class Cache<TKey, TResult> : ICache<TKey, TResult> {
private readonly Dictionary<TKey, CacheEntry> _entries;
public Cache() {
_entries = new Dictionary<TKey, CacheEntry>();
}
#region Implementation of ICache<TKey,TResult>
public TResult Get(TKey key, Func<AcquireContext, TResult> acquire) {
CacheEntry entry;
if (!_entries.TryGetValue(key, out entry)) {
AcquireContext context = new AcquireContext();
entry = new CacheEntry {Result = acquire(context)};
_entries.Add(key, entry);
}
return entry.Result;
}
#endregion
public class CacheEntry {
public TResult Result { get; set; }
}
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using Castle.Core;
namespace Orchard.Caching {
public class DefaultCacheManager : ICacheManager {
private readonly Dictionary<Pair<Type, Type>, object> _caches;
public DefaultCacheManager() {
_caches = new Dictionary<Pair<Type, Type>, object>();
}
#region Implementation of ICacheManager
public TResult Get<TKey, TResult>(TKey key, Func<AcquireContext, TResult> acquire) {
return GetCache<TKey, TResult>().Get(key, acquire);
}
public ICache<TKey, TResult> GetCache<TKey, TResult>() {
var cacheKey = new Pair<Type, Type>(typeof(TKey), typeof(TResult));
object value;
if (!_caches.TryGetValue(cacheKey, out value)) {
value = new Cache<TKey, TResult>();
_caches.Add(cacheKey, value);
}
return (ICache<TKey, TResult>)value;
}
#endregion
}
}

View File

@@ -0,0 +1,7 @@
using System;
namespace Orchard.Caching {
public interface ICache<TKey, TResult> {
TResult Get(TKey key, Func<AcquireContext, TResult> acquire);
}
}

View File

@@ -0,0 +1,8 @@
using System;
namespace Orchard.Caching {
public interface ICacheManager : ISingletonDependency {
TResult Get<TKey, TResult>(TKey key, Func<AcquireContext, TResult> acquire);
ICache<TKey, TResult> GetCache<TKey, TResult>();
}
}

View File

@@ -131,6 +131,11 @@
<Compile Include="Validation\Argument.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="Caching\AcquireContext.cs" />
<Compile Include="Caching\Cache.cs" />
<Compile Include="Caching\DefaultCacheManager.cs" />
<Compile Include="Caching\ICache.cs" />
<Compile Include="Caching\ICacheManager.cs" />
<Compile Include="Commands\CommandParameters.cs" />
<Compile Include="Commands\CommandDescriptor.cs" />
<Compile Include="Commands\CommandHandlerDescriptor.cs" />