Compare commits

...

46 Commits

Author SHA1 Message Date
Lombiq
3dc6e6523f Blocking requests to tenants being restarted, thus not allowing users to see temporary YSODs 2016-03-19 19:54:46 +01:00
Sébastien Ros
740039d6ef Merge pull request #6601 from jtkech/patch-16
Fixes 1.10.x compilation errors
2016-03-17 16:42:13 -07:00
jtkech
e30c86d0a5 Update TermAdminController.cs 2016-03-17 23:50:08 +01:00
jtkech
559985b29c Update DefaultOrchardHost.cs 2016-03-17 23:47:28 +01:00
Sebastien Ros
d39485abf2 Merge branch '1.9.x' into 1.10.x
Conflicts:
	src/Orchard.Specs/MultiTenancy.feature
	src/Orchard.Web/Config/HostComponents.config
	src/Orchard.Web/Modules/Orchard.Taxonomies/Controllers/TermAdminController.cs
	src/Orchard/Environment/DefaultOrchardHost.cs
	src/Rebracer.xml
2016-03-17 10:18:46 -07:00
Sebastien Ros
c4dd5a9e18 Updating Rebracer settings 2016-03-16 16:34:45 -07:00
Sebastien Ros
9a2c08d31e Fixing tenant load retry logic 2016-03-16 16:34:32 -07:00
Sébastien Ros
dd326127ce Merge pull request #6582 from infofromca/typo
Fixed a typo in DefaultOrchardShell.cs
2016-03-14 15:32:35 -07:00
andy zheng
82c9baad18 fixed a typo in defaultorchardShell 2016-03-14 18:06:10 -04:00
Sébastien Ros
2273d38a7f Merge pull request #6568 from OrchardCMS/sebros/etag
Adding correct ETag support
2016-03-11 10:46:38 -08:00
Sebastien Ros
181db18355 Adding correct ETag support 2016-03-11 10:45:57 -08:00
Sébastien Ros
32bf5ed6c8 Merge pull request #6569 from jtkech/patch-6
[Fixes #6544] NRE with containable part & Orchard.Lists
2016-03-11 09:28:55 -08:00
jtkech
af7da7963e Update List.cshtml 2016-03-10 23:28:08 +01:00
jtkech
3b7a54f10d Update Shapes.cs 2016-03-10 23:26:25 +01:00
Sébastien Ros
5e6a1549ad Merge pull request #6567 from jtkech/patch-5
Fixing multi-tenancy functional tests
2016-03-10 14:06:10 -08:00
jtkech
447728df03 Update MultiTenancy.feature 2016-03-10 22:47:51 +01:00
jtkech
30f163bd39 Update MultiTenancy.feature.cs 2016-03-10 22:46:19 +01:00
Sébastien Ros
78bab529d2 Merge pull request #6566 from carlwoodhouse/1.9.x
[Fixes #6523] Output cache route config
2016-03-10 13:00:34 -08:00
Carl Woodhouse
31ed21b061 Output cache route config fixes #6523 2016-03-10 20:31:55 +00:00
Sebastien Ros
712ff2f487 Retrying on tenant startup
Conflicts:
	src/Orchard.Web/Config/HostComponents.config
	src/Orchard/Environment/DefaultOrchardHost.cs
2016-03-09 15:39:57 -08:00
Sebastien Ros
aae9f6270e [Fixes #6526] Unwrap TargetInvocationException when executing a ShapeAttributeBinding 2016-03-09 15:37:44 -08:00
Sébastien Ros
26e2fe9977 Merge pull request #6419 from rtpHarry/fix-6272
[Fixes #6272] Improve wording for comparisons
2016-03-03 12:41:25 -08:00
Sébastien Ros
b1c606becc Merge pull request #6426 from rtpHarry/terms-mentions-taxonomies
Fix wrong permission check and incorrect use of taxonomies phrase
2016-03-03 12:37:27 -08:00
Sébastien Ros
8b8cf0b7e6 Merge pull request #6427 from rtpHarry/module-list-flexbox
[Fixes #5907] Convert modules admin page to use css flexbox
2016-03-03 12:36:36 -08:00
Sébastien Ros
7effab96a0 Merge pull request #6429 from rtpHarry/fix-5706
[Fixes #5706] Force order on Properties, Validation, Bindings
2016-03-03 12:33:38 -08:00
Sébastien Ros
1b8bcb8a4e Merge pull request #6500 from infofromca/tenantPrefixHost
[Fixes #6495] Ensure a host or a url is defined for new tenants
2016-03-03 12:26:53 -08:00
andy zheng
026acc4aa9 fixed one of URL prefix and Host should be required 2016-02-28 13:30:11 -05:00
Sébastien Ros
ad6a2b96e5 Merge pull request #6428 from rtpHarry/fix-packaging-display-glitch
Fixing display glitch in admin packaging/gallery/modules
2016-02-25 13:06:44 -08:00
Sébastien Ros
72c9f0063b Merge pull request #6433 from rtpHarry/fix-5708
[Fixes #5708] Display mandatory checkbox as required
2016-02-19 15:50:52 -08:00
Matthew Harris
a8d4b18f72 fixes #5708 - change ui layer to display Mandatory checkbox as required 2016-02-19 22:44:52 +00:00
Matthew Harris
1907e57902 fixes #5706 - standardize order on Properties, Validation, Bindings 2016-02-19 02:27:23 +00:00
Matthew Harris
a806ac7487 fix display glitch in admin packaging/gallery/modules 2016-02-19 01:56:33 +00:00
Sébastien Ros
47072f91ae Merge pull request #6425 from rtpHarry/widget-ruleengine-typo
Fix typo in Orchard.Widgets.RuleEngine.cs
2016-02-18 17:50:45 -08:00
Matthew Harris
bbe4110053 fix typo in typo fix 2016-02-19 01:43:55 +00:00
Matthew Harris
5dfda7c0a8 convert modules admin page to use css flexbox 2016-02-19 01:36:28 +00:00
Matthew Harris
89a5953ea3 fix wrong permission check and incorrect use of taxonomies phrase 2016-02-19 00:45:54 +00:00
Matthew Harris
c61666bfdc fix typo in Orchard.Widgets.RuleEngine.cs 2016-02-19 00:32:40 +00:00
Sébastien Ros
2749f2fa21 Merge pull request #6424 from rtpHarry/fix-taxonomy-permission
fixes #6423 - wrong permission checked for delete
2016-02-18 16:30:53 -08:00
Matthew Harris
fe5c8ba233 fixes #6423 - wrong permission checked for delete 2016-02-19 00:27:53 +00:00
Sébastien Ros
290f8e4594 Merge pull request #6420 from rtpHarry/fix-6400
fixes #6400 - improve notifier messages for terms admin
2016-02-18 16:05:49 -08:00
Matthew Harris
5811598dff fixes 6400 - improve notifier messages for terms admin 2016-02-18 22:01:52 +00:00
Matthew Harris
2a01771a37 fixes #6272 update wording for comparisons 2016-02-18 21:44:57 +00:00
Sébastien Ros
a1531da11b Merge pull request #6301 from Codinlab/fixes/5593
Fixes #5593 : Allow disabling HtmlEncode on DynamicForms
2016-02-18 12:55:31 -08:00
Lombiq
49022cd1b5 To the previous: also caring about the case when ShapePart was dynamically added to the content type/item, see #6360 2016-02-18 16:32:16 +01:00
Lombiq
4cc48a380d You can't create two Template content items with the same name (title) any more, fixes #6360 2016-02-17 21:05:14 +01:00
Thierry Fleury
f3971484b8 Fixes #5593 : Dynamic Forms: In form settings HtmlEncode option is always checked 2016-01-23 10:32:22 +01:00
28 changed files with 334 additions and 87 deletions

View File

@@ -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 "<h3>Scott\s*</h3>"
@@ -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 "<li class="tenant Uninitialized">"
@@ -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"
And I should see "Request URL host: example.org"

View File

@@ -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

View File

@@ -120,5 +120,13 @@
</Properties>
</Component>
<Component Type="Orchard.Environment.DefaultOrchardHost">
<Properties>
<!-- The number of retries when a tenant can't be loaded -->
<Property Name="Retries" Value="1"/>
<Property Name="DelayRetries" Value="true"/>
</Properties>
</Component>
</Components>
</HostComponents>

View File

@@ -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);
}

View File

@@ -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",

View File

@@ -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",

View File

@@ -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); }
}

View File

@@ -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);
}
}

View File

@@ -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 {
});
}
}
}
}

View File

@@ -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 @@
</div>
@if (Model.ListNavigation != null) {
@Display(Model.ListNavigation)
}
}

View File

@@ -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%;
}

View File

@@ -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);
}

View File

@@ -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<ShellSettings>().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<IOutputCacheStorageProvider>();
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; }
}
}
}

View File

@@ -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; }

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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:

View File

@@ -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:

View File

@@ -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);

View File

@@ -4,6 +4,7 @@ using System.Linq;
using System.Web.Mvc;
using Orchard.Taxonomies.Models;
using Orchard.Localization;
using Orchard.Mvc.Html;
using Orchard.Taxonomies.ViewModels;
using Orchard.Taxonomies.Services;
using Orchard.UI.Admin;
@@ -70,15 +71,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 +110,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 +215,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 +228,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);

View File

@@ -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<TitlePart> {
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<ShapePart>()) {
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<TitlePartRecord>(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;
}
}
}

View File

@@ -180,6 +180,7 @@
<ItemGroup>
<Compile Include="AdminMenu.cs" />
<Compile Include="Controllers\AdminController.cs" />
<Compile Include="Drivers\TitlePartDriver.cs" />
<Compile Include="Helpers\StringExtensions.cs" />
<Compile Include="Handlers\ShapePartHandler.cs" />
<Compile Include="Drivers\ShapePartDriver.cs" />

View File

@@ -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) {

View File

@@ -14,7 +14,11 @@ using Orchard.Logging;
using Orchard.Mvc;
using Orchard.Mvc.Extensions;
using Orchard.Utility.Extensions;
using Orchard.Utility;
using Orchard.Exceptions;
using System.Threading;
using System.Web;
using Orchard.Mvc;
namespace Orchard.Environment {
// All the event handlers that DefaultOrchardHost implements have to be declared in OrchardStarter.
@@ -30,10 +34,14 @@ namespace Orchard.Environment {
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly static object _syncLock = new object();
private readonly static object _shellContextsWriteLock = new object();
private readonly NamedReaderWriterLock _shellActivationLock = new NamedReaderWriterLock();
private IEnumerable<ShellContext> _shellContexts;
private readonly ContextState<IList<ShellSettings>> _tenantsToRestart;
public int Retries { get; set; }
public bool DelayRetries { get; set; }
public DefaultOrchardHost(
IShellSettingsManager shellSettingsManager,
IShellContextFactory shellContextFactory,
@@ -142,16 +150,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();
@@ -245,10 +271,30 @@ namespace Orchard.Environment {
protected virtual void BeginRequest() {
BlockRequestsDuringSetup();
// Ensure all shell contexts are loaded, or need to be reloaded if
// extensions have changed
MonitorExtensions();
BuildCurrent();
Action ensureInitialized = () => {
// Ensure all shell contexts are loaded, or need to be reloaded if
// extensions have changed
MonitorExtensions();
BuildCurrent();
};
ShellSettings currentShellSettings = null;
var httpContext = _httpContextAccessor.Current();
if (httpContext != null) {
currentShellSettings = _runningShellTable.Match(httpContext);
}
if (currentShellSettings == null) {
ensureInitialized();
}
else {
_shellActivationLock.RunWithReadLock(currentShellSettings.Name, () => {
ensureInitialized();
});
}
// StartUpdatedShells can cause a writer shell activation lock so it should run outside the reader lock.
StartUpdatedShells();
}
@@ -306,19 +352,21 @@ namespace Orchard.Environment {
}
// reload the shell as its settings have changed
else {
// dispose previous context
shellContext.Shell.Terminate();
_shellActivationLock.RunWithWriteLock(settings.Name, () => {
// dispose previous context
shellContext.Shell.Terminate();
var context = _shellContextFactory.CreateShellContext(settings);
var context = _shellContextFactory.CreateShellContext(settings);
// Activate and register modified context.
// Forcing enumeration with ToArray() so a lazy execution isn't causing issues by accessing the disposed shell context.
_shellContexts = _shellContexts.Where(shell => shell.Settings.Name != settings.Name).Union(new[] { context }).ToArray();
// Activate and register modified context.
// Forcing enumeration with ToArray() so a lazy execution isn't causing issues by accessing the disposed shell context.
_shellContexts = _shellContexts.Where(shell => shell.Settings.Name != settings.Name).Union(new[] { context }).ToArray();
shellContext.Dispose();
context.Shell.Activate();
shellContext.Dispose();
context.Shell.Activate();
_runningShellTable.Update(settings);
_runningShellTable.Update(settings);
});
}
}

View File

@@ -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");
}
}
}

View File

@@ -699,6 +699,7 @@
<Compile Include="Messaging\Services\IMessagingChannel.cs" />
<Compile Include="IWorkContextAccessor.cs" />
<Compile Include="Utility\Extensions\VirtualPathProviderExtensions.cs" />
<Compile Include="Utility\NamedReaderWriterLock.cs" />
<Compile Include="Utility\ReflectionHelper.cs" />
<Compile Include="Validation\PathValidation.cs" />
<Compile Include="Wcf\OrchardDependencyInjectionServiceBehavior.cs" />

View File

@@ -0,0 +1,84 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Orchard.Utility {
/// <summary>
/// Provides locking similar to <see cref="ReaderWriterLockSlim"/> but
/// </summary>
/// <remarks>
/// Taken from http://johnculviner.com/achieving-named-lock-locker-functionality-in-c-4-0/ and adapted a bit. Namely:
/// - <see cref="GetOrAdd"/> uses a new ReaderWriterLockSlim to overcome possible concurrency issues where the factory delegate could run multiple times.
/// - Implemented <see cref="IDisposable"/>.
/// </remarks>
public class NamedReaderWriterLock : IDisposable {
private readonly ConcurrentDictionary<string, ReaderWriterLockSlim> _lockDictonary = new ConcurrentDictionary<string, ReaderWriterLockSlim>();
public ReaderWriterLockSlim GetLock(string name) {
return _lockDictonary.GetOrAdd(name, new ReaderWriterLockSlim());
}
public TResult RunWithReadLock<TResult>(string name, Func<TResult> body) {
var rwLock = GetLock(name);
try {
rwLock.EnterReadLock();
return body();
}
finally {
rwLock.ExitReadLock();
}
}
public void RunWithReadLock(string name, Action body) {
var rwLock = GetLock(name);
try {
rwLock.EnterReadLock();
body();
}
finally {
rwLock.ExitReadLock();
}
}
public TResult RunWithWriteLock<TResult>(string name, Func<TResult> body) {
var rwLock = GetLock(name);
try {
rwLock.EnterWriteLock();
return body();
}
finally {
rwLock.ExitWriteLock();
}
}
public void RunWithWriteLock(string name, Action body) {
var rwLock = GetLock(name);
try {
rwLock.EnterWriteLock();
body();
}
finally {
rwLock.ExitWriteLock();
}
}
public void RemoveLock(string name) {
ReaderWriterLockSlim o;
_lockDictonary.TryRemove(name, out o);
}
/// <summary>
/// Disposes all the internal <see cref="ReaderWriterLockSlim"/> objects. Only call this if you're sure that no concurrent code executes
/// any other instance method of this class!
/// </summary>
public void Dispose() {
foreach (var lockSlim in _lockDictonary.Values) {
lockSlim.Dispose();
}
}
}
}

View File

@@ -130,6 +130,7 @@
<PropertyValue name="AutoIndentOnTab">false</PropertyValue>
<PropertyValue name="ColorizeInactiveBlocksDifferently">true</PropertyValue>
<PropertyValue name="DisableBrowsingUpToDateCheck">true</PropertyValue>
<PropertyValue name="DisableCreateDeclDefnScan">false</PropertyValue>
<PropertyValue name="DisableErrorReporting">false</PropertyValue>
<PropertyValue name="DisableIntelliSenseErrorsInErrorList">false</PropertyValue>
<PropertyValue name="EnableChangeSignature">false</PropertyValue>
@@ -154,6 +155,7 @@
<PropertyValue name="IndentPreprocessor">2</PropertyValue>
<PropertyValue name="IndentationReference">2</PropertyValue>
<PropertyValue name="MemberListDotToArrow">false</PropertyValue>
<PropertyValue name="MemberListFilterHeuristic">false</PropertyValue>
<PropertyValue name="NewlineControlBlockBrace">0</PropertyValue>
<PropertyValue name="NewlineEmptyFunctionCloseBrace">true</PropertyValue>
<PropertyValue name="NewlineEmptyTypeCloseBrace">true</PropertyValue>
@@ -235,6 +237,7 @@
<PropertyValue name="ShowVirtualProjectsInSolutionExplorerWhenSolutionOpen">false</PropertyValue>
<PropertyValue name="UseAMDCodeGenerationForModulesThatAreNotPartOfAProject">false</PropertyValue>
<PropertyValue name="UseCommonJSCodeGenerationForModulesThatAreNotPartOfAProject">false</PropertyValue>
<PropertyValue name="UseES2015CodeGenerationForModulesThatAreNotPartOfAProject">false</PropertyValue>
<PropertyValue name="UseJsxPreserveForFilesThatAreNotPartOfAProject">false</PropertyValue>
<PropertyValue name="UseJsxReactForFilesThatAreNotPartOfAProject">false</PropertyValue>
<PropertyValue name="UseSystemCodeGenerationForModulesThatAreNotPartOfAProject">false</PropertyValue>
@@ -245,6 +248,7 @@
<PropertyValue name="EnableValidation">true</PropertyValue>
<PropertyValue name="ErrorsAsWarnings">true</PropertyValue>
<PropertyValue name="FormatOnPaste">true</PropertyValue>
<PropertyValue name="IdentifyHelpfulExtensions">true</PropertyValue>
<PropertyValue name="InsertAttributeValueQuotes">true</PropertyValue>
<PropertyValue name="InsertClosingTag">true</PropertyValue>
<PropertyValue name="SuggestHelpfulExtensions">false</PropertyValue>