diff --git a/src/Orchard.Specs/MultiTenancy.feature b/src/Orchard.Specs/MultiTenancy.feature
index 7134c19c9..4701fd9ef 100644
--- a/src/Orchard.Specs/MultiTenancy.feature
+++ b/src/Orchard.Specs/MultiTenancy.feature
@@ -25,6 +25,7 @@ Scenario: A new tenant is created
And I fill in
| name | value |
| Name | Scott |
+ | RequestUrlPrefix | scott |
And I hit "Save"
And I am redirected
Then I should see "
Scott\s*
"
@@ -37,6 +38,7 @@ Scenario: A new tenant is created with uninitialized state
And I fill in
| name | value |
| Name | Scott |
+ | RequestUrlPrefix | scott |
And I hit "Save"
And I am redirected
Then I should see ""
@@ -199,4 +201,4 @@ Scenario: Listing tenants from command line
And I have tenant "Alpha" on "example.org" as "New-site-name"
When I execute >tenant list
Then I should see "Name: Alpha"
- And I should see "Request URL host: example.org"
\ No newline at end of file
+ And I should see "Request URL host: example.org"
diff --git a/src/Orchard.Specs/MultiTenancy.feature.cs b/src/Orchard.Specs/MultiTenancy.feature.cs
index f2867c62f..5cee8d1d6 100644
--- a/src/Orchard.Specs/MultiTenancy.feature.cs
+++ b/src/Orchard.Specs/MultiTenancy.feature.cs
@@ -129,6 +129,9 @@ this.ScenarioSetup(scenarioInfo);
table1.AddRow(new string[] {
"Name",
"Scott"});
+ table1.AddRow(new string[] {
+ "RequestUrlPrefix",
+ "scott"});
#line 25
testRunner.And("I fill in", ((string)(null)), table1, "And ");
#line 28
@@ -163,6 +166,9 @@ this.ScenarioSetup(scenarioInfo);
table2.AddRow(new string[] {
"Name",
"Scott"});
+ table2.AddRow(new string[] {
+ "RequestUrlPrefix",
+ "scott"});
#line 37
testRunner.And("I fill in", ((string)(null)), table2, "And ");
#line 40
diff --git a/src/Orchard.Web/Config/HostComponents.config b/src/Orchard.Web/Config/HostComponents.config
index 0e3fddd25..da8b3838a 100644
--- a/src/Orchard.Web/Config/HostComponents.config
+++ b/src/Orchard.Web/Config/HostComponents.config
@@ -120,5 +120,13 @@
+
+
+
+
+
+
+
+
diff --git a/src/Orchard.Web/Modules/Orchard.DynamicForms/Drivers/BindingsElementDriver.cs b/src/Orchard.Web/Modules/Orchard.DynamicForms/Drivers/BindingsElementDriver.cs
index f30894420..fa7c2ee13 100644
--- a/src/Orchard.Web/Modules/Orchard.DynamicForms/Drivers/BindingsElementDriver.cs
+++ b/src/Orchard.Web/Modules/Orchard.DynamicForms/Drivers/BindingsElementDriver.cs
@@ -37,7 +37,7 @@ namespace Orchard.DynamicForms.Drivers {
var bindingsEditor = context.ShapeFactory.EditorTemplate(TemplateName: "FormBindings", Model: viewModel);
- bindingsEditor.Metadata.Position = "Bindings:10";
+ bindingsEditor.Metadata.Position = "Bindings:20";
return Editor(context, bindingsEditor);
}
diff --git a/src/Orchard.Web/Modules/Orchard.DynamicForms/Drivers/CheckboxElementDriver.cs b/src/Orchard.Web/Modules/Orchard.DynamicForms/Drivers/CheckboxElementDriver.cs
index 25392e55c..beb915651 100644
--- a/src/Orchard.Web/Modules/Orchard.DynamicForms/Drivers/CheckboxElementDriver.cs
+++ b/src/Orchard.Web/Modules/Orchard.DynamicForms/Drivers/CheckboxElementDriver.cs
@@ -45,9 +45,9 @@ namespace Orchard.DynamicForms.Drivers {
_IsRequired: shape.Checkbox(
Id: "IsMandatory",
Name: "IsMandatory",
- Title: "Mandatory",
+ Title: "Required",
Value: "true",
- Description: T("Tick this checkbox to make this check box element mandatory.")),
+ Description: T("Tick this checkbox to make this check box element required.")),
_CustomValidationMessage: shape.Textbox(
Id: "CustomValidationMessage",
Name: "CustomValidationMessage",
diff --git a/src/Orchard.Web/Modules/Orchard.DynamicForms/Drivers/FormElementDriver.cs b/src/Orchard.Web/Modules/Orchard.DynamicForms/Drivers/FormElementDriver.cs
index 16ad4c8fa..f054e4d4d 100644
--- a/src/Orchard.Web/Modules/Orchard.DynamicForms/Drivers/FormElementDriver.cs
+++ b/src/Orchard.Web/Modules/Orchard.DynamicForms/Drivers/FormElementDriver.cs
@@ -82,7 +82,6 @@ namespace Orchard.DynamicForms.Drivers {
Name: "HtmlEncode",
Title: "Html Encode",
Value: "true",
- Checked: true,
Description: T("Check this option to automatically HTML encode submitted values to prevent code injection.")),
_CreateContent: shape.Checkbox(
Id: "CreateContent",
diff --git a/src/Orchard.Web/Modules/Orchard.DynamicForms/Elements/Form.cs b/src/Orchard.Web/Modules/Orchard.DynamicForms/Elements/Form.cs
index 2c6c44996..cd9c211db 100644
--- a/src/Orchard.Web/Modules/Orchard.DynamicForms/Elements/Form.cs
+++ b/src/Orchard.Web/Modules/Orchard.DynamicForms/Elements/Form.cs
@@ -33,7 +33,7 @@ namespace Orchard.DynamicForms.Elements {
}
public bool HtmlEncode {
- get { return this.Retrieve(x => x.HtmlEncode, () => true); }
+ get { return this.Retrieve(x => x.HtmlEncode); }
set { this.Store(x => x.HtmlEncode, value); }
}
diff --git a/src/Orchard.Web/Modules/Orchard.DynamicForms/ValidationRules/Mandatory.cs b/src/Orchard.Web/Modules/Orchard.DynamicForms/ValidationRules/Mandatory.cs
index b5092943c..9193fc953 100644
--- a/src/Orchard.Web/Modules/Orchard.DynamicForms/ValidationRules/Mandatory.cs
+++ b/src/Orchard.Web/Modules/Orchard.DynamicForms/ValidationRules/Mandatory.cs
@@ -19,7 +19,7 @@ namespace Orchard.DynamicForms.ValidationRules {
private LocalizedString GetValidationMessage(ValidationContext context) {
return String.IsNullOrWhiteSpace(ErrorMessage)
- ? T("{0} is a mandatory field.", context.FieldName)
+ ? T("{0} is a required field.", context.FieldName)
: T(ErrorMessage);
}
}
diff --git a/src/Orchard.Web/Modules/Orchard.Lists/Shapes.cs b/src/Orchard.Web/Modules/Orchard.Lists/Shapes.cs
index 0f0edeece..405dc40a3 100644
--- a/src/Orchard.Web/Modules/Orchard.Lists/Shapes.cs
+++ b/src/Orchard.Web/Modules/Orchard.Lists/Shapes.cs
@@ -24,6 +24,8 @@ namespace Orchard.Lists {
builder.Describe("ListNavigation").OnDisplaying(context => {
var containable = (ContainablePart) context.Shape.ContainablePart;
var container = _containerService.Value.GetContainer(containable, VersionOptions.Latest);
+ if (container == null) return;
+
var previous = _containerService.Value.Previous(container.Id, containable);
var next = _containerService.Value.Next(container.Id, containable);
@@ -32,4 +34,4 @@ namespace Orchard.Lists {
});
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Orchard.Web/Modules/Orchard.Lists/Views/Admin/List.cshtml b/src/Orchard.Web/Modules/Orchard.Lists/Views/Admin/List.cshtml
index af414e8fd..7b32b3643 100644
--- a/src/Orchard.Web/Modules/Orchard.Lists/Views/Admin/List.cshtml
+++ b/src/Orchard.Web/Modules/Orchard.Lists/Views/Admin/List.cshtml
@@ -3,7 +3,7 @@
@using Orchard.ContentManagement;
@{
Style.Require("jQueryColorBox");
- Style.Include("nprogress.css", "nprogress.min.css");
+ Style.Include("nprogress.css");
Style.Include("common-admin.css", "common-admin.min.css");
Style.Include("list-admin.css", "list-admin.min.css");
Script.Require("ContentPicker").AtFoot();
@@ -39,4 +39,4 @@
@if (Model.ListNavigation != null) {
@Display(Model.ListNavigation)
-}
\ No newline at end of file
+}
diff --git a/src/Orchard.Web/Modules/Orchard.Modules/styles/orchard-modules-admin.css b/src/Orchard.Web/Modules/Orchard.Modules/styles/orchard-modules-admin.css
index 88b146380..09b1e59c3 100644
--- a/src/Orchard.Web/Modules/Orchard.Modules/styles/orchard-modules-admin.css
+++ b/src/Orchard.Web/Modules/Orchard.Modules/styles/orchard-modules-admin.css
@@ -6,18 +6,21 @@ html.dyn #main ul.features button { display:none; }
.features.detail-view .category > ul {
border:1px solid #EAEAEA;
margin-bottom:2em;
+ display: flex;
+ flex-direction: column;
}
.features.summary-view .category {
- overflow:hidden;
padding-bottom:1em;
}
+
+.features.summary-view .category > ul {
+ display: flex;
+ flex-wrap: wrap;
+}
+
.features.summary-view .feature {
border:1px solid #EAEAEA;
- display:block;
- float:left;
- height:6em;
margin:0 .5% 1% .5%;
- position:relative;
width:32%;
}
diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Controllers/AdminController.cs b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Controllers/AdminController.cs
index 6cf12eb82..a3fef98d3 100644
--- a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Controllers/AdminController.cs
+++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Controllers/AdminController.cs
@@ -70,6 +70,10 @@ namespace Orchard.MultiTenancy.Controllers {
ModelState.AddModelError("Name", T("Invalid tenant name. Must contain characters only and no spaces.").Text);
}
+ if (!string.Equals(viewModel.Name, "default", StringComparison.OrdinalIgnoreCase) && string.IsNullOrWhiteSpace( viewModel.RequestUrlHost) && string.IsNullOrWhiteSpace(viewModel.RequestUrlPrefix)) {
+ ModelState.AddModelError("RequestUrlHostRequestUrlPrefix", T("RequestUrlHost and RequestUrlPrefix can not be empty at the same time.").Text);
+ }
+
if (!ModelState.IsValid) {
return View(viewModel);
}
@@ -143,6 +147,10 @@ namespace Orchard.MultiTenancy.Controllers {
if (tenant == null)
return HttpNotFound();
+ if (!string.Equals(viewModel.Name, "default", StringComparison.OrdinalIgnoreCase) && string.IsNullOrWhiteSpace(viewModel.RequestUrlHost) && string.IsNullOrWhiteSpace(viewModel.RequestUrlPrefix)) {
+ ModelState.AddModelError("RequestUrlHostRequestUrlPrefix", T("RequestUrlHost and RequestUrlPrefix can not be empty at the same time.").Text);
+ }
+
if (!ModelState.IsValid) {
return View(viewModel);
}
diff --git a/src/Orchard.Web/Modules/Orchard.OutputCache/Filters/OutputCacheFilter.cs b/src/Orchard.Web/Modules/Orchard.OutputCache/Filters/OutputCacheFilter.cs
index 860236eef..00013de70 100644
--- a/src/Orchard.Web/Modules/Orchard.OutputCache/Filters/OutputCacheFilter.cs
+++ b/src/Orchard.Web/Modules/Orchard.OutputCache/Filters/OutputCacheFilter.cs
@@ -73,6 +73,7 @@ namespace Orchard.OutputCache.Filters {
// State.
private CacheSettings _cacheSettings;
+ private CacheRouteConfig _cacheRouteConfig;
private DateTime _now;
private WorkContext _workContext;
private string _cacheKey;
@@ -94,6 +95,13 @@ namespace Orchard.OutputCache.Filters {
_now = _clock.UtcNow;
_workContext = _workContextAccessor.GetContext();
+ var configurations = _cacheService.GetRouteConfigs();
+ if (configurations.Any()) {
+ var route = filterContext.Controller.ControllerContext.RouteData.Route;
+ var key = _cacheService.GetRouteDescriptorKey(filterContext.HttpContext, route);
+ _cacheRouteConfig = configurations.FirstOrDefault(c => c.RouteKey == key);
+ }
+
if (!RequestIsCacheable(filterContext))
return;
@@ -182,16 +190,8 @@ namespace Orchard.OutputCache.Filters {
Logger.Debug("Item '{0}' was rendered.", _cacheKey);
- // Obtain individual route configuration, if any.
- CacheRouteConfig configuration = null;
- var configurations = _cacheService.GetRouteConfigs();
- if (configurations.Any()) {
- var route = filterContext.Controller.ControllerContext.RouteData.Route;
- var key = _cacheService.GetRouteDescriptorKey(filterContext.HttpContext, route);
- configuration = configurations.FirstOrDefault(c => c.RouteKey == key);
- }
-
- if (!ResponseIsCacheable(filterContext, configuration)) {
+
+ if (!ResponseIsCacheable(filterContext)) {
filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
filterContext.HttpContext.Response.Cache.SetNoStore();
filterContext.HttpContext.Response.Cache.SetMaxAge(new TimeSpan(0));
@@ -199,8 +199,8 @@ namespace Orchard.OutputCache.Filters {
}
// Determine duration and grace time.
- var cacheDuration = configuration != null && configuration.Duration.HasValue ? configuration.Duration.Value : CacheSettings.DefaultCacheDuration;
- var cacheGraceTime = configuration != null && configuration.GraceTime.HasValue ? configuration.GraceTime.Value : CacheSettings.DefaultCacheGraceTime;
+ var cacheDuration = _cacheRouteConfig != null && _cacheRouteConfig.Duration.HasValue ? _cacheRouteConfig.Duration.Value : CacheSettings.DefaultCacheDuration;
+ var cacheGraceTime = _cacheRouteConfig != null && _cacheRouteConfig.GraceTime.HasValue ? _cacheRouteConfig.GraceTime.Value : CacheSettings.DefaultCacheGraceTime;
// Include each content item ID as tags for the cache entry.
var contentItemIds = _displayedContentItemHandler.GetDisplayed().Select(x => x.ToString(CultureInfo.InvariantCulture)).ToArray();
@@ -209,6 +209,15 @@ namespace Orchard.OutputCache.Filters {
var response = filterContext.HttpContext.Response;
var captureStream = new CaptureStream(response.Filter);
response.Filter = captureStream;
+
+ // Add ETag header for the newly created item
+ var etag = Guid.NewGuid().ToString("n");
+ if (HttpRuntime.UsingIntegratedPipeline) {
+ if (response.Headers.Get("ETag") == null) {
+ response.Headers["ETag"] = etag;
+ }
+ }
+
captureStream.Captured += (output) => {
try {
// Since this is a callback any call to injected dependencies can result in an Autofac exception: "Instances
@@ -229,12 +238,12 @@ namespace Orchard.OutputCache.Filters {
Url = filterContext.HttpContext.Request.Url.AbsolutePath,
Tenant = scope.Resolve().Name,
StatusCode = response.StatusCode,
- Tags = new[] { _invariantCacheKey }.Union(contentItemIds).ToArray()
+ Tags = new[] { _invariantCacheKey }.Union(contentItemIds).ToArray(),
+ ETag = etag
};
// Write the rendered item to the cache.
var cacheStorageProvider = scope.Resolve();
- cacheStorageProvider.Remove(_cacheKey);
cacheStorageProvider.Set(_cacheKey, cacheItem);
Logger.Debug("Item '{0}' was written to cache.", _cacheKey);
@@ -314,6 +323,12 @@ namespace Orchard.OutputCache.Filters {
return false;
}
+ // Don't cache if individual route configuration says no.
+ if (_cacheRouteConfig != null && _cacheRouteConfig.Duration == 0) {
+ Logger.Debug("Request for item '{0}' ignored because route is configured to not be cached.", itemDescriptor);
+ return false;
+ }
+
// Ignore requests with the refresh key on the query string.
foreach (var key in filterContext.RequestContext.HttpContext.Request.QueryString.AllKeys) {
if (String.Equals(_refreshKey, key, StringComparison.OrdinalIgnoreCase)) {
@@ -325,7 +340,7 @@ namespace Orchard.OutputCache.Filters {
return true;
}
- protected virtual bool ResponseIsCacheable(ResultExecutedContext filterContext, CacheRouteConfig configuration) {
+ protected virtual bool ResponseIsCacheable(ResultExecutedContext filterContext) {
if (filterContext.HttpContext.Request.Url == null) {
return false;
@@ -337,12 +352,6 @@ namespace Orchard.OutputCache.Filters {
return false;
}
- // Don't cache in individual route configuration says no.
- if (configuration != null && configuration.Duration == 0) {
- Logger.Debug("Response for item '{0}' will not be cached because route is configured to not be cached.", _cacheKey);
- return false;
- }
-
// Don't cache if request created notifications.
var hasNotifications = !String.IsNullOrEmpty(Convert.ToString(filterContext.Controller.TempData["messages"]));
if (hasNotifications) {
@@ -467,6 +476,7 @@ namespace Orchard.OutputCache.Filters {
private void ServeCachedItem(ActionExecutingContext filterContext, CacheItem cacheItem) {
var response = filterContext.HttpContext.Response;
+ var request = filterContext.HttpContext.Request;
// Fix for missing charset in response headers
response.Charset = response.Charset;
@@ -476,12 +486,27 @@ namespace Orchard.OutputCache.Filters {
response.AddHeader("X-Cached-On", cacheItem.CachedOnUtc.ToString("r"));
response.AddHeader("X-Cached-Until", cacheItem.ValidUntilUtc.ToString("r"));
}
-
+
// Shorcut action execution.
filterContext.Result = new FileContentResult(cacheItem.Output, cacheItem.ContentType);
-
response.StatusCode = cacheItem.StatusCode;
+ // Add ETag header
+ if (HttpRuntime.UsingIntegratedPipeline && response.Headers.Get("ETag") == null) {
+ response.Headers["ETag"] = cacheItem.ETag;
+ }
+
+ // Check ETag in request
+ // https://www.w3.org/2005/MWI/BPWG/techs/CachingWithETag.html
+ var etag = request.Headers["If-None-Match"];
+ if (!String.IsNullOrEmpty(etag)) {
+ if (String.Equals(etag, cacheItem.ETag, StringComparison.Ordinal)) {
+ // ETag matches the cached item, we return a 304
+ filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.NotModified);
+ return;
+ }
+ }
+
ApplyCacheControl(response);
}
@@ -511,15 +536,6 @@ namespace Orchard.OutputCache.Filters {
// response.DisableKernelCache();
// response.Cache.SetOmitVaryStar(true);
- // An ETag is a string that uniquely identifies a specific version of a component.
- // We use the cache item to detect if it's a new one.
- if (HttpRuntime.UsingIntegratedPipeline) {
- if (response.Headers.Get("ETag") == null) {
- // What is the point of GetHashCode() of a newly generated item? /DanielStolt
- response.Cache.SetETag(new CacheItem().GetHashCode().ToString(CultureInfo.InvariantCulture));
- }
- }
-
if (CacheSettings.VaryByQueryStringParameters == null) {
response.Cache.VaryByParams["*"] = true;
}
@@ -628,4 +644,4 @@ namespace Orchard.OutputCache.Filters {
public class ViewDataContainer : IViewDataContainer {
public ViewDataDictionary ViewData { get; set; }
}
-}
\ No newline at end of file
+}
diff --git a/src/Orchard.Web/Modules/Orchard.OutputCache/Models/CacheItem.cs b/src/Orchard.Web/Modules/Orchard.OutputCache/Models/CacheItem.cs
index 29243df6f..aeeff8af0 100644
--- a/src/Orchard.Web/Modules/Orchard.OutputCache/Models/CacheItem.cs
+++ b/src/Orchard.Web/Modules/Orchard.OutputCache/Models/CacheItem.cs
@@ -4,7 +4,7 @@ namespace Orchard.OutputCache.Models {
[Serializable]
public class CacheItem {
// used for serialization compatibility
- public static readonly string Version = "1";
+ public static readonly string Version = "2";
public DateTime CachedOnUtc { get; set; }
public int Duration { get; set; }
@@ -18,6 +18,7 @@ namespace Orchard.OutputCache.Models {
public string Tenant { get; set; }
public int StatusCode { get; set; }
public string[] Tags { get; set; }
+ public string ETag { get; set; }
public int ValidFor {
get { return Duration; }
diff --git a/src/Orchard.Web/Modules/Orchard.OutputCache/Services/DefaultCacheStorageProvider.cs b/src/Orchard.Web/Modules/Orchard.OutputCache/Services/DefaultCacheStorageProvider.cs
index 13d684659..9f219fd74 100644
--- a/src/Orchard.Web/Modules/Orchard.OutputCache/Services/DefaultCacheStorageProvider.cs
+++ b/src/Orchard.Web/Modules/Orchard.OutputCache/Services/DefaultCacheStorageProvider.cs
@@ -16,6 +16,7 @@ namespace Orchard.OutputCache.Services {
}
public void Set(string key, CacheItem cacheItem) {
+ _workContext.HttpContext.Cache.Remove(key);
_workContext.HttpContext.Cache.Add(
key,
cacheItem,
diff --git a/src/Orchard.Web/Modules/Orchard.Packaging/Styles/orchard-packaging-admin.css b/src/Orchard.Web/Modules/Orchard.Packaging/Styles/orchard-packaging-admin.css
index 8e7e369b7..e8c93ea06 100644
--- a/src/Orchard.Web/Modules/Orchard.Packaging/Styles/orchard-packaging-admin.css
+++ b/src/Orchard.Web/Modules/Orchard.Packaging/Styles/orchard-packaging-admin.css
@@ -11,7 +11,7 @@
float:left;
}
.extensionName.installed {
- background: url("images/installed.gif") no-repeat 0px 8px #fff;
+ background: url("images/installed.gif") no-repeat 0px 8px;
padding:0 0 0 60px;
}
.contentItems .related {
diff --git a/src/Orchard.Web/Modules/Orchard.Projections/FilterEditors/Forms/DateTimeFilterForm.cs b/src/Orchard.Web/Modules/Orchard.Projections/FilterEditors/Forms/DateTimeFilterForm.cs
index 28dbe68e9..cbca13934 100644
--- a/src/Orchard.Web/Modules/Orchard.Projections/FilterEditors/Forms/DateTimeFilterForm.cs
+++ b/src/Orchard.Web/Modules/Orchard.Projections/FilterEditors/Forms/DateTimeFilterForm.cs
@@ -251,7 +251,7 @@ namespace Orchard.Projections.FilterEditors.Forms {
case DateTimeOperator.LessThan:
return T("{0} is less than {1}{2}", fieldName, value, T(valueUnit));
case DateTimeOperator.LessThanEquals:
- return T("{0} is less or equal than {1}{2}", fieldName, value, T(valueUnit));
+ return T("{0} is less than or equal to {1}{2}", fieldName, value, T(valueUnit));
case DateTimeOperator.Equals:
return T("{0} equals {1}{2}", fieldName, value, T(valueUnit));
case DateTimeOperator.NotEquals:
@@ -259,7 +259,7 @@ namespace Orchard.Projections.FilterEditors.Forms {
case DateTimeOperator.GreaterThan:
return T("{0} is greater than {1}{2}", fieldName, value, T(valueUnit));
case DateTimeOperator.GreaterThanEquals:
- return T("{0} is greater or equal than {1}{2}", fieldName, value, T(valueUnit));
+ return T("{0} is greater than or equal to {1}{2}", fieldName, value, T(valueUnit));
case DateTimeOperator.Between:
return T("{0} is between {1}{2} and {3}{4}", fieldName, min, T(minUnit), max, T(maxUnit));
case DateTimeOperator.NotBetween:
diff --git a/src/Orchard.Web/Modules/Orchard.Projections/FilterEditors/Forms/NumericFilterForm.cs b/src/Orchard.Web/Modules/Orchard.Projections/FilterEditors/Forms/NumericFilterForm.cs
index eb37f44d9..f0ead2a0d 100644
--- a/src/Orchard.Web/Modules/Orchard.Projections/FilterEditors/Forms/NumericFilterForm.cs
+++ b/src/Orchard.Web/Modules/Orchard.Projections/FilterEditors/Forms/NumericFilterForm.cs
@@ -128,7 +128,7 @@ namespace Orchard.Projections.FilterEditors.Forms {
case NumericOperator.LessThan:
return T("{0} is less than {1}", fieldName, value);
case NumericOperator.LessThanEquals:
- return T("{0} is less or equal than {1}", fieldName, value);
+ return T("{0} is less than or equal to {1}", fieldName, value);
case NumericOperator.Equals:
return T("{0} equals {1}", fieldName, value);
case NumericOperator.NotEquals:
@@ -136,7 +136,7 @@ namespace Orchard.Projections.FilterEditors.Forms {
case NumericOperator.GreaterThan:
return T("{0} is greater than {1}", fieldName, value);
case NumericOperator.GreaterThanEquals:
- return T("{0} is greater or equal than {1}", fieldName, value);
+ return T("{0} is greater than or equal to {1}", fieldName, value);
case NumericOperator.Between:
return T("{0} is between {1} and {2}", fieldName, min, max);
case NumericOperator.NotBetween:
diff --git a/src/Orchard.Web/Modules/Orchard.Taxonomies/Controllers/AdminController.cs b/src/Orchard.Web/Modules/Orchard.Taxonomies/Controllers/AdminController.cs
index 962ffa310..394c58ebf 100644
--- a/src/Orchard.Web/Modules/Orchard.Taxonomies/Controllers/AdminController.cs
+++ b/src/Orchard.Web/Modules/Orchard.Taxonomies/Controllers/AdminController.cs
@@ -85,7 +85,7 @@ namespace Orchard.Taxonomies.Controllers {
[HttpPost]
public ActionResult Delete(int id) {
- if (!Services.Authorizer.Authorize(Permissions.CreateTaxonomy, T("Couldn't delete taxonomy")))
+ if (!Services.Authorizer.Authorize(Permissions.ManageTaxonomies, T("Couldn't delete taxonomy")))
return new HttpUnauthorizedResult();
var taxonomy = _taxonomyService.GetTaxonomy(id);
diff --git a/src/Orchard.Web/Modules/Orchard.Taxonomies/Controllers/TermAdminController.cs b/src/Orchard.Web/Modules/Orchard.Taxonomies/Controllers/TermAdminController.cs
index 2b9c1cc94..d85f224ef 100644
--- a/src/Orchard.Web/Modules/Orchard.Taxonomies/Controllers/TermAdminController.cs
+++ b/src/Orchard.Web/Modules/Orchard.Taxonomies/Controllers/TermAdminController.cs
@@ -70,15 +70,24 @@ namespace Orchard.Taxonomies.Controllers {
var checkedEntries = viewModel.Terms.Where(t => t.IsChecked).ToList();
switch (viewModel.BulkAction) {
case TermsAdminIndexBulkAction.None:
+ Services.Notifier.Information(T("No action selected."));
break;
case TermsAdminIndexBulkAction.Delete:
if (!Services.Authorizer.Authorize(Permissions.ManageTerms, T("Couldn't delete term")))
return new HttpUnauthorizedResult();
+
+ if(!checkedEntries.Any()) {
+ Services.Notifier.Information(T("No terms selected."));
+ break;
+ }
foreach (var entry in checkedEntries) {
var term = _taxonomyService.GetTerm(entry.Id);
_taxonomyService.DeleteTerm(term);
}
+
+ Services.Notifier.Information(T.Plural("{0} term has been removed.", "{0} terms have been removed.", checkedEntries.Count));
+
break;
case TermsAdminIndexBulkAction.Merge:
if (!Services.Authorizer.Authorize(Permissions.ManageTerms, T("Couldn't delete term")))
@@ -100,8 +109,6 @@ namespace Orchard.Taxonomies.Controllers {
throw new ArgumentOutOfRangeException();
}
- Services.Notifier.Information(T("{0} term have been removed.", checkedEntries.Count));
-
return RedirectToAction("Index", new { taxonomyId = viewModel.TaxonomyId });
}
@@ -207,7 +214,7 @@ namespace Orchard.Taxonomies.Controllers {
public ActionResult Edit(int id) {
- if (!Services.Authorizer.Authorize(Permissions.ManageTerms, T("Not allowed to manage taxonomies")))
+ if (!Services.Authorizer.Authorize(Permissions.ManageTerms, T("Not allowed to manage terms")))
return new HttpUnauthorizedResult();
var term = _taxonomyService.GetTerm(id);
@@ -220,7 +227,7 @@ namespace Orchard.Taxonomies.Controllers {
[HttpPost, ActionName("Edit")]
public ActionResult EditPost(int id) {
- if (!Services.Authorizer.Authorize(Permissions.ManageTaxonomies, T("Couldn't edit taxonomy")))
+ if (!Services.Authorizer.Authorize(Permissions.ManageTerms, T("Couldn't edit term")))
return new HttpUnauthorizedResult();
var term = _taxonomyService.GetTerm(id);
diff --git a/src/Orchard.Web/Modules/Orchard.Templates/Drivers/TitlePartDriver.cs b/src/Orchard.Web/Modules/Orchard.Templates/Drivers/TitlePartDriver.cs
new file mode 100644
index 000000000..2a7b6c37c
--- /dev/null
+++ b/src/Orchard.Web/Modules/Orchard.Templates/Drivers/TitlePartDriver.cs
@@ -0,0 +1,49 @@
+using Orchard.ContentManagement;
+using Orchard.ContentManagement.Drivers;
+using Orchard.Core.Title.Models;
+using Orchard.Localization;
+using Orchard.Templates.Models;
+using System.Linq;
+
+namespace Orchard.Templates.Drivers {
+ public class TitlePartDriver : ContentPartDriver {
+ private readonly IContentManager _contentManager;
+
+ public Localizer T { get; set; }
+
+ public TitlePartDriver(IContentManager contentManager) {
+ _contentManager = contentManager;
+
+ T = NullLocalizer.Instance;
+ }
+
+ protected override DriverResult Editor(TitlePart part, IUpdateModel updater, dynamic shapeHelper) {
+ if (!part.ContentItem.Has()) {
+ return null;
+ }
+
+ updater.TryUpdateModel(part, Prefix, null, null);
+
+ // We need to query for the content type names because querying for content parts has no effect on the database side.
+ var contentTypesWithShapePart = _contentManager
+ .GetContentTypeDefinitions()
+ .Where(typeDefinition => typeDefinition.Parts.Any(partDefinition => partDefinition.PartDefinition.Name == "ShapePart"))
+ .Select(typeDefinition => typeDefinition.Name);
+
+ // If ShapePart is only dynamically added to this content type or even this content item then we won't find
+ // a corresponding content type definition, so using the current content type too.
+ contentTypesWithShapePart = contentTypesWithShapePart.Union(new[] { part.ContentItem.ContentType });
+
+ var existingShapeCount = _contentManager
+ .Query(VersionOptions.Latest, contentTypesWithShapePart.ToArray())
+ .Where(record => record.Title == part.Title && record.ContentItemRecord.Id != part.ContentItem.Id)
+ .Count();
+
+ if (existingShapeCount > 0) {
+ updater.AddModelError("ShapeNameAlreadyExists", T("A template with the given name already exists."));
+ }
+
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Orchard.Web/Modules/Orchard.Templates/Orchard.Templates.csproj b/src/Orchard.Web/Modules/Orchard.Templates/Orchard.Templates.csproj
index 6aed37fb9..d8ebcb7be 100644
--- a/src/Orchard.Web/Modules/Orchard.Templates/Orchard.Templates.csproj
+++ b/src/Orchard.Web/Modules/Orchard.Templates/Orchard.Templates.csproj
@@ -180,6 +180,7 @@
+
diff --git a/src/Orchard/DisplayManagement/Descriptors/ShapeAttributeStrategy/ShapeAttributeBindingStrategy.cs b/src/Orchard/DisplayManagement/Descriptors/ShapeAttributeStrategy/ShapeAttributeBindingStrategy.cs
index 22ad79ca7..3dbe111e8 100644
--- a/src/Orchard/DisplayManagement/Descriptors/ShapeAttributeStrategy/ShapeAttributeBindingStrategy.cs
+++ b/src/Orchard/DisplayManagement/Descriptors/ShapeAttributeStrategy/ShapeAttributeBindingStrategy.cs
@@ -60,12 +60,18 @@ namespace Orchard.DisplayManagement.Descriptors.ShapeAttributeStrategy {
var output = new HtmlStringWriter();
var arguments = methodInfo.GetParameters()
.Select(parameter => BindParameter(displayContext, parameter, output));
-
- var returnValue = methodInfo.Invoke(serviceInstance, arguments.ToArray());
- if (methodInfo.ReturnType != typeof(void)) {
- output.Write(CoerceHtmlString(returnValue));
+ try {
+ var returnValue = methodInfo.Invoke(serviceInstance, arguments.ToArray());
+ if (methodInfo.ReturnType != typeof(void)) {
+ output.Write(CoerceHtmlString(returnValue));
+ }
+ return output;
+ }
+ catch(TargetInvocationException e) {
+ // Throwing a TIE here will probably kill the web process
+ // in Azure. For unknown reasons.
+ throw e.InnerException;
}
- return output;
}
private static IHtmlString CoerceHtmlString(object invoke) {
diff --git a/src/Orchard/Environment/DefaultOrchardHost.cs b/src/Orchard/Environment/DefaultOrchardHost.cs
index ec86af817..c14be633a 100644
--- a/src/Orchard/Environment/DefaultOrchardHost.cs
+++ b/src/Orchard/Environment/DefaultOrchardHost.cs
@@ -15,6 +15,7 @@ using Orchard.Mvc;
using Orchard.Mvc.Extensions;
using Orchard.Utility.Extensions;
using Orchard.Exceptions;
+using System.Threading;
namespace Orchard.Environment {
// All the event handlers that DefaultOrchardHost implements have to be declared in OrchardStarter.
@@ -34,6 +35,9 @@ namespace Orchard.Environment {
private IEnumerable _shellContexts;
private readonly ContextState> _tenantsToRestart;
+
+ public bool DelayRetries { get; set; }
+
public DefaultOrchardHost(
IShellSettingsManager shellSettingsManager,
IShellContextFactory shellContextFactory,
@@ -142,16 +146,34 @@ namespace Orchard.Environment {
// Load all tenants, and activate their shell.
if (allSettings.Any()) {
Parallel.ForEach(allSettings, settings => {
- try {
- var context = CreateShellContext(settings);
- ActivateShell(context);
- }
- catch (Exception ex) {
- if (ex.IsFatal()) {
- throw;
- }
- Logger.Error(ex, "A tenant could not be started: " + settings.Name);
+ for (var i = 0; i <= Retries; i++) {
+
+ // Not the first attempt, wait for a while ...
+ if (DelayRetries && i > 0) {
+
+ // Wait for i^2 which means 1, 2, 4, 8 ... seconds
+ Thread.Sleep(TimeSpan.FromSeconds(Math.Pow(i, 2)));
+ }
+
+ try {
+ var context = CreateShellContext(settings);
+ ActivateShell(context);
+
+ // If everything went well, return to stop the retry loop
+ return;
+ }
+ catch (Exception ex) {
+ if (i == Retries) {
+ Logger.Fatal("A tenant could not be started: {0} after {1} retries.", settings.Name, Retries);
+ return;
+ }
+ else {
+ Logger.Error(ex, "A tenant could not be started: " + settings.Name + " Attempt number: " + i);
+ }
+ }
+
}
+
while (_processingEngine.AreTasksPending()) {
Logger.Debug("Processing pending task after activate Shell");
_processingEngine.ExecuteNextTask();
diff --git a/src/Orchard/Environment/DefaultOrchardShell.cs b/src/Orchard/Environment/DefaultOrchardShell.cs
index dcca7e9a6..618c8d9a9 100644
--- a/src/Orchard/Environment/DefaultOrchardShell.cs
+++ b/src/Orchard/Environment/DefaultOrchardShell.cs
@@ -104,7 +104,7 @@ namespace Orchard.Environment {
throw;
}
- Logger.Error(ex, "An unexcepted error occured while terminating the Shell");
+ Logger.Error(ex, "An unexpected error occured while terminating the Shell");
}
}
}
diff --git a/src/Rebracer.xml b/src/Rebracer.xml
index df145c351..9d83e6ac7 100644
--- a/src/Rebracer.xml
+++ b/src/Rebracer.xml
@@ -130,6 +130,7 @@
false
true
true
+ false
false
false
false
@@ -154,6 +155,7 @@
2
2
false
+ false
0
true
true
@@ -235,6 +237,7 @@
false
false
false
+ false
false
false
false
@@ -245,6 +248,7 @@
true
true
true
+ true
true
true
false