Merge remote-tracking branch 'origin/dev' into feature/tenantstateinitializing

This commit is contained in:
Sipke Schoorstra
2015-09-10 23:39:01 +01:00
84 changed files with 821 additions and 232 deletions

View File

@@ -13,7 +13,8 @@ var glob = require("glob"),
uglify = require("gulp-uglify"),
rename = require("gulp-rename"),
concat = require("gulp-concat"),
header = require("gulp-header");
header = require("gulp-header"),
fs = require("fs");
/*
** GULP TASKS
@@ -37,32 +38,53 @@ gulp.task("rebuild", function () {
return merge(assetGroupTasks);
});
// Continuous watch (each asset group is built whenever one of its inputs changes).
// Set "Watchers" as sub-processes in order to restart the task when Assets.json changes.
gulp.task("watch", function () {
var pathWin32 = require("path");
getAssetGroups().forEach(function (assetGroup) {
var watchPaths = assetGroup.inputPaths.concat(assetGroup.watchPaths);
gulp.watch(watchPaths, function (event) {
var isConcat = path.basename(assetGroup.outputFileName, path.extname(assetGroup.outputFileName)) !== "@";
if (isConcat)
console.log("Asset file '" + event.path + "' was " + event.type + ", rebuilding asset group with output '" + assetGroup.outputPath + "'.");
else
console.log("Asset file '" + event.path + "' was " + event.type + ", rebuilding asset group.");
var doRebuild = true;
var task = createAssetGroupTask(assetGroup, doRebuild);
var watchers;
function restart() {
if (watchers) {
watchers.forEach(function (w) {
w.remove();
w.end();
});
}
watchers = [];
// Continuous watch (each asset group is built whenever one of its inputs changes).
getAssetGroups().forEach(function (assetGroup) {
var watchPaths = assetGroup.inputPaths.concat(assetGroup.watchPaths);
var watcher = gulp.watch(watchPaths, function (event) {
var isConcat = path.basename(assetGroup.outputFileName, path.extname(assetGroup.outputFileName)) !== "@";
if (isConcat)
console.log("Asset file '" + event.path + "' was " + event.type + ", rebuilding asset group with output '" + assetGroup.outputPath + "'.");
else
console.log("Asset file '" + event.path + "' was " + event.type + ", rebuilding asset group.");
var doRebuild = true;
var task = createAssetGroupTask(assetGroup, doRebuild);
});
watchers.push(watcher);
});
}
var p;
if (p) { p.exit(); }
p = gulp.watch("Orchard.Web/{Core,Modules,Themes}/*/Assets.json", function (event) {
console.log("Asset file '" + event.path + "' was " + event.type + ", resetting asset watchers.");
restart();
});
restart();
});
/*
** ASSET GROUPS
*/
function getAssetGroups() {
var assetManifestPaths = glob.sync("Orchard.Web/{Core,Modules,Themes}/*/Assets.json");
var assetManifestPaths = glob.sync("Orchard.Web/{Core,Modules,Themes}/*/Assets.json", {});
var assetGroups = [];
assetManifestPaths.forEach(function (assetManifestPath) {
var assetManifest = require("./" + assetManifestPath);
var file = './' + assetManifestPath;
var json = fs.readFileSync(file, 'utf8');
assetManifest = eval(json);
assetManifest.forEach(function (assetGroup) {
resolveAssetGroupPaths(assetGroup, assetManifestPath);
assetGroups.push(assetGroup);

View File

@@ -110,23 +110,23 @@ namespace Orchard.Tests.Modules.Recipes.Services {
[Test]
public void HarvestRecipesFailsToFindRecipesWhenCalledWithNotExistingExtension() {
var recipes = (List<Recipe>)_recipeHarvester.HarvestRecipes("cantfindme");
var recipes = _recipeHarvester.HarvestRecipes("cantfindme");
Assert.That(recipes.Count, Is.EqualTo(0));
Assert.That(recipes.Count(), Is.EqualTo(0));
}
[Test]
public void HarvestRecipesShouldHarvestRecipeXmlFiles() {
var recipes = (List<Recipe>)_recipeHarvester.HarvestRecipes("Sample1");
Assert.That(recipes.Count, Is.EqualTo(1));
var recipes = _recipeHarvester.HarvestRecipes("Sample1");
Assert.That(recipes.Count(), Is.EqualTo(1));
}
[Test]
public void ParseRecipeLoadsRecipeMetaDataIntoModel() {
var recipes = (List<Recipe>)_recipeHarvester.HarvestRecipes("Sample1");
Assert.That(recipes.Count, Is.EqualTo(1));
var recipes = _recipeHarvester.HarvestRecipes("Sample1");
Assert.That(recipes.Count(), Is.EqualTo(1));
var sampleRecipe = recipes[0];
var sampleRecipe = recipes.First();
Assert.That(sampleRecipe.Name, Is.EqualTo("cms"));
Assert.That(sampleRecipe.Description, Is.EqualTo("a sample Orchard recipe describing a cms"));
Assert.That(sampleRecipe.Author, Is.EqualTo("orchard"));

View File

@@ -1,4 +1,6 @@
using System;
using System.Linq;
using System.Threading;
using Autofac;
using NUnit.Framework;
using Orchard.Caching;
@@ -78,6 +80,42 @@ namespace Orchard.Tests.Caching {
Is.Not.SameAs(c2.CacheManager.GetCache<string, string>()));
}
[Test]
public void CacheManagerIsNotBlocking() {
var hits = 0;
string result = "";
Enumerable.Range(0, 5).AsParallel().ForAll(x =>
result = _cacheManager.Get("testItem", ctx => {
// by waiting for 100ms we expect all the calls to Get
// to enter this lambda
Thread.Sleep(100);
hits++;
return "testResult";
})
);
Assert.That(result, Is.EqualTo("testResult"));
Assert.That(hits, Is.GreaterThan(1));
}
[Test]
public void CacheManagerIsBlocking() {
var hits = 0;
string result = "";
Enumerable.Range(0, 5).AsParallel().ForAll(x =>
result = _cacheManager.Get("testItem", true, ctx => {
Thread.Sleep(100);
hits++;
return "testResult";
})
);
Assert.That(result, Is.EqualTo("testResult"));
Assert.That(hits, Is.EqualTo(1));
}
class ComponentOne {
public ICacheManager CacheManager { get; set; }

View File

@@ -4,7 +4,7 @@ using Orchard.Localization.Models;
namespace Orchard.Tests.Localization {
[TestFixture]
[TestFixture()]
public class DateTimePartsTests {
[Test]

View File

@@ -1,6 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
@@ -13,7 +12,8 @@ using Orchard.Localization.Services;
namespace Orchard.Tests.Localization {
[TestFixture]
[TestFixture()]
[Category("longrunning")]
public class DefaultDateFormatterTests {
[SetUp]

View File

@@ -9,7 +9,7 @@ namespace Orchard.Tests.Stubs {
WorkContext = workContext;
}
public WorkContext WorkContext { get; }
public WorkContext WorkContext { get; private set; }
public void Dispose() {
_lifetimeScope.Dispose();

View File

@@ -89,9 +89,9 @@ namespace Orchard.WarmupStarter {
var result = _initialization(application);
_initializationResult = result;
}
catch (Exception e) {
catch (Exception ex) {
lock (_synLock) {
_error = e;
_error = ex;
_previousError = null;
}
}

View File

@@ -17,6 +17,7 @@ using Orchard.UI.Navigation;
using Orchard.Utility;
using System;
using Orchard.Logging;
using Orchard.Exceptions;
namespace Orchard.Core.Navigation.Controllers {
[ValidateInput(false)]
@@ -179,6 +180,10 @@ namespace Orchard.Core.Navigation.Controllers {
return View(model);
}
catch (Exception exception) {
if (exception.IsFatal()) {
throw;
}
Logger.Error(T("Creating menu item failed: {0}", exception.Message).Text);
Services.Notifier.Error(T("Creating menu item failed: {0}", exception.Message));
return this.RedirectLocal(returnUrl, () => RedirectToAction("Index"));

View File

@@ -11,6 +11,7 @@ using Orchard.Security.Permissions;
using Orchard.UI;
using Orchard.UI.Navigation;
using Orchard.Utility;
using Orchard.Exceptions;
namespace Orchard.Core.Navigation.Services {
public class NavigationManager : INavigationManager {
@@ -152,6 +153,9 @@ namespace Orchard.Core.Navigation.Services {
items = builder.Build();
}
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
Logger.Error(ex, "Unexpected error while querying a navigation provider. It was ignored. The menu provided by the provider may not be complete.");
}
if (items != null) {
@@ -170,6 +174,9 @@ namespace Orchard.Core.Navigation.Services {
items = builder.Build();
}
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
Logger.Error(ex, "Unexpected error while querying a menu provider. It was ignored. The menu provided by the provider may not be complete.");
}
if (items != null) {
@@ -188,6 +195,9 @@ namespace Orchard.Core.Navigation.Services {
imageSets = builder.BuildImageSets();
}
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
Logger.Error(ex, "Unexpected error while querying a navigation provider. It was ignored. The menu provided by the provider may not be complete.");
}
if (imageSets != null) {

View File

@@ -8,6 +8,7 @@ using Orchard.Logging;
using Orchard.Services;
using Orchard.Tasks;
using Orchard.Tasks.Scheduling;
using Orchard.Exceptions;
namespace Orchard.Core.Scheduling.Services {
public class ScheduledTaskExecutor : IBackgroundTask {
@@ -63,6 +64,9 @@ namespace Orchard.Core.Scheduling.Services {
}
}
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
Logger.Warning(ex, "Unable to process scheduled task #{0} of type {1}", taskEntry.Id, taskEntry.Action);
_transactionManager.Cancel();
}

View File

@@ -10,6 +10,7 @@ using Orchard.Logging;
using Orchard.Security;
using Orchard.Settings;
using Orchard.UI.Notify;
using Orchard.Exceptions;
namespace Orchard.Core.Settings.Drivers {
public class SiteSettingsPartDriver : ContentPartDriver<SiteSettingsPart> {
@@ -100,9 +101,12 @@ namespace Orchard.Core.Settings.Drivers {
using (request.GetResponse() as HttpWebResponse) {}
}
}
catch (Exception e) {
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
_notifier.Warning(T("The base url you entered could not be requested from current location."));
Logger.Warning(e, "Could not query base url: {0}", model.Site.BaseUrl);
Logger.Warning(ex, "Could not query base url: {0}", model.Site.BaseUrl);
}
}
}

View File

@@ -9,6 +9,7 @@ using Orchard.ContentManagement.MetaData.Services;
using Orchard.Core.Settings.Metadata.Records;
using Orchard.Data;
using Orchard.Logging;
using Orchard.Exceptions;
namespace Orchard.Core.Settings.Metadata {
public class ContentDefinitionManager : Component, IContentDefinitionManager {
@@ -124,7 +125,7 @@ namespace Orchard.Core.Settings.Metadata {
}
private IDictionary<string, ContentTypeDefinition> AcquireContentTypeDefinitions() {
return _cacheManager.Get("ContentTypeDefinitions", ctx => {
return _cacheManager.Get("ContentTypeDefinitions", true, ctx => {
MonitorContentDefinitionSignal(ctx);
AcquireContentPartDefinitions();
@@ -139,7 +140,7 @@ namespace Orchard.Core.Settings.Metadata {
}
private IDictionary<string, ContentPartDefinition> AcquireContentPartDefinitions() {
return _cacheManager.Get("ContentPartDefinitions", ctx => {
return _cacheManager.Get("ContentPartDefinitions", true, ctx => {
MonitorContentDefinitionSignal(ctx);
var contentPartDefinitionRecords = _partDefinitionRepository.Table
@@ -152,7 +153,7 @@ namespace Orchard.Core.Settings.Metadata {
}
private IDictionary<string, ContentFieldDefinition> AcquireContentFieldDefinitions() {
return _cacheManager.Get("ContentFieldDefinitions", ctx => {
return _cacheManager.Get("ContentFieldDefinitions", true, ctx => {
MonitorContentDefinitionSignal(ctx);
return _fieldDefinitionRepository.Table.Select(Build).ToDictionary(x => x.Name, y => y);
@@ -282,6 +283,9 @@ namespace Orchard.Core.Settings.Metadata {
return XElement.Parse(settings);
}
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
Logger.Error(ex, "Unable to parse settings xml");
return null;
}

View File

@@ -17,7 +17,7 @@ namespace Orchard.Core.Settings.Services {
}
public ISite GetSiteSettings() {
var siteId = _cacheManager.Get("SiteId", ctx => {
var siteId = _cacheManager.Get("SiteId", true, ctx => {
var site = _contentManager.Query("Site")
.List()
.FirstOrDefault();

View File

@@ -1,5 +1,4 @@
using System;
using System.Globalization;
using System;
using System.Web;
using System.Web.Mvc;
using Orchard.DisplayManagement;
@@ -32,8 +31,20 @@ namespace Orchard.Core.Shapes {
DateTimeUtc = DateTimeUtc != System.DateTime.MinValue ? DateTimeUtc : dateTimeUtc; // Both capitalizations retained for compatibility.
var time = _clock.UtcNow - DateTimeUtc;
if (time.TotalDays > 7 || time.TotalDays < -7)
return Display.DateTime(DateTimeUtc: DateTimeUtc, CustomFormat: null);
if (time.TotalYears() > 1)
return T.Plural("1 year ago", "{0} years ago", time.TotalYears());
if (time.TotalYears() < -1)
return T.Plural("in 1 year", "in {0} years", -time.TotalYears());
if (time.TotalMonths() > 1)
return T.Plural("1 month ago", "{0} months ago", time.TotalMonths());
if (time.TotalMonths() < -1)
return T.Plural("in 1 month", "in {0} months", -time.TotalMonths());
if (time.TotalWeeks() > 1)
return T.Plural("1 week ago", "{0} weeks ago", time.TotalWeeks());
if (time.TotalWeeks() < -1)
return T.Plural("in 1 week", "in {0} weeks", -time.TotalWeeks());
if (time.TotalHours > 24)
return T.Plural("1 day ago", "{0} days ago", time.Days);
@@ -71,4 +82,18 @@ namespace Orchard.Core.Shapes {
return new MvcHtmlString(_dateLocalizationServices.ConvertToLocalizedString(DateTimeUtc, CustomFormat.Text));
}
}
}
public static class TimespanExtensions {
public static int TotalWeeks(this TimeSpan time) {
return (int)time.TotalDays / 7;
}
public static int TotalMonths(this TimeSpan time) {
return (int)time.TotalDays / 31;
}
public static int TotalYears(this TimeSpan time) {
return (int)time.TotalDays / 365;
}
}
}

View File

@@ -17,7 +17,7 @@ namespace Orchard.ContentPicker.Fields {
public IEnumerable<ContentItem> ContentItems {
get {
return _contentItems.Value;
return _contentItems.Value ?? Enumerable.Empty<ContentItem>();
}
}

View File

@@ -22,7 +22,7 @@ namespace Orchard.ContentTypes.Services {
}
public IEnumerable<StereotypeDescription> GetStereotypes() {
return _cacheManager.Get("ContentType.Stereotypes", context => {
return _cacheManager.Get("ContentType.Stereotypes", true, context => {
// TODO: Implement a signal in ContentDefinitionManager that gets raised whenever a type definition is updated.
// For now, we'll just cache the stereotypes for 1 minute.

View File

@@ -1,10 +1,15 @@
using System;
using System.Linq;
using System.Security.Cryptography;
using Orchard.ContentManagement.MetaData;
using Orchard.Core.Contents.Extensions;
using Orchard.Data;
using Orchard.Data.Migration;
namespace Orchard.DynamicForms {
public class Migrations : DataMigrationImpl {
private readonly byte[] _oldLayoutHash = new byte[] { 0x91, 0x10, 0x3b, 0x97, 0xce, 0x1e, 0x1e, 0xc7, 0x7a, 0x41, 0xf7, 0x82, 0xe8, 0x58, 0x85, 0x91 };
private const string DefaultFormLayoutData =
@"{
""elements"": [
@@ -59,7 +64,52 @@ namespace Orchard.DynamicForms {
.WithSetting("LayoutTypePartSettings.DefaultLayoutData", DefaultFormLayoutData))
.WithSetting("Stereotype", "Widget")
.DisplayedAs("Form Widget"));
return 1;
return 2;
}
public int UpdateFrom1() {
// if the default layout data was unchanged, fix it with the new default
var formLayoutPart = ContentDefinitionManager
.GetTypeDefinition("Form")
.Parts
.FirstOrDefault(x => x.PartDefinition.Name == "LayoutPart");
if (formLayoutPart != null &&
formLayoutPart.Settings["LayoutTypePartSettings.DefaultLayoutData"] != null) {
var layout = formLayoutPart.Settings["LayoutTypePartSettings.DefaultLayoutData"];
if(GetMD5(layout) == _oldLayoutHash) {
ContentDefinitionManager.AlterTypeDefinition("Form", type => type
.WithPart("LayoutPart", p => p
.WithSetting("LayoutTypePartSettings.DefaultLayoutData", DefaultFormLayoutData))
);
}
}
var formWidgetLayoutPart = ContentDefinitionManager
.GetTypeDefinition("FormWidget")
.Parts
.FirstOrDefault(x => x.PartDefinition.Name == "LayoutPart");
if (formWidgetLayoutPart != null &&
formWidgetLayoutPart.Settings["LayoutTypePartSettings.DefaultLayoutData"] != null) {
var layout = formWidgetLayoutPart.Settings["LayoutTypePartSettings.DefaultLayoutData"];
if (GetMD5(layout) == _oldLayoutHash) {
ContentDefinitionManager.AlterTypeDefinition("FormWidget", type => type
.WithPart("LayoutPart", p => p
.WithSetting("LayoutTypePartSettings.DefaultLayoutData", DefaultFormLayoutData))
);
}
}
return 2;
}
private byte[] GetMD5(string text) {
byte[] encodedText = System.Text.Encoding.UTF8.GetBytes(text);
return ((HashAlgorithm)CryptoConfig.CreateFromName("MD5")).ComputeHash(encodedText);
}
}
}

View File

@@ -1,7 +1,7 @@
Name: Custom Forms
Name: Dynamic Forms
AntiForgery: enabled
Author: The Orchard Team
Website: http://orchardcustomforms.codeplex.com
Website: http://www.orchardproject.net/
Version: 1.9.1
OrchardVersion: 1.9
Description: Create custom forms like contact forms using layouts.

View File

@@ -7,7 +7,7 @@ namespace Orchard.DynamicForms.Tokens {
public void Describe(DescribeContext context) {
context.For("FormSubmission", T("Dynamic Form submission"), T("Dynamic Form Submission tokens for use in workflows handling the Dynamic Form Submitted event."))
.Token("Field:*", T("Field:<field name>"), T("The posted field value to access."))
.Token("Field:*", T("Field:<field name>"), T("The posted field value to access."), "Text")
.Token("IsValid:*", T("IsValid:<field name>"), T("The posted field validation status."))
;
}
@@ -15,10 +15,20 @@ namespace Orchard.DynamicForms.Tokens {
public void Evaluate(EvaluateContext context) {
context.For<FormSubmissionTokenContext>("FormSubmission")
.Token(token => token.StartsWith("Field:", StringComparison.OrdinalIgnoreCase) ? token.Substring("Field:".Length) : null, GetFieldValue)
.Chain(FilterChainParam, "Text", GetFieldValue)
.Token(token => token.StartsWith("IsValid:", StringComparison.OrdinalIgnoreCase) ? token.Substring("IsValid:".Length) : null, GetFieldValidationStatus);
}
private object GetFieldValue(string fieldName, FormSubmissionTokenContext context) {
private static Tuple<string, string> FilterChainParam(string token) {
int tokenLength = "Field:".Length;
int chainIndex = token.IndexOf('.');
if (token.StartsWith("Field:", StringComparison.OrdinalIgnoreCase) && chainIndex > tokenLength)
return new Tuple<string, string>(token.Substring(tokenLength, chainIndex - tokenLength), token.Substring(chainIndex + 1));
else
return null;
}
private string GetFieldValue(string fieldName, FormSubmissionTokenContext context) {
return context.PostedValues[fieldName];
}

View File

@@ -307,10 +307,9 @@ namespace Orchard.Layouts.Drivers {
var queryPart = query.As<QueryPart>();
var layoutIndex = XmlHelper.Parse<int>(context.ExportableData.Get("LayoutIndex"));
var layout = queryPart.Layouts[layoutIndex];
element.QueryId = queryPart.Id;
element.LayoutId = layout.Id;
element.LayoutId = layoutIndex != -1 ? queryPart.Layouts[layoutIndex].Id : -1;
}
private static string GetLayoutDescription(IEnumerable<LayoutDescriptor> layouts, LayoutRecord l) {

View File

@@ -38,7 +38,7 @@ namespace Orchard.Layouts.Services {
public IEnumerable<ElementDescriptor> DescribeElements(DescribeElementsContext context) {
var contentType = context.Content != null ? context.Content.ContentItem.ContentType : default(string);
var cacheKey = String.Format("LayoutElementTypes-{0}-{1}", contentType ?? "AnyType", context.CacheVaryParam);
return _cacheManager.Get(cacheKey, acquireContext => {
return _cacheManager.Get(cacheKey, true, acquireContext => {
var harvesterContext = new HarvestElementsContext {
Content = context.Content
};
@@ -55,7 +55,7 @@ namespace Orchard.Layouts.Services {
public IEnumerable<CategoryDescriptor> GetCategories(DescribeElementsContext context) {
var contentType = context.Content != null ? context.Content.ContentItem.ContentType : default(string);
return _cacheManager.Get(String.Format("ElementCategories-{0}-{1}", contentType ?? "AnyType", context.CacheVaryParam), acquireContext => {
return _cacheManager.Get(String.Format("ElementCategories-{0}-{1}", contentType ?? "AnyType", context.CacheVaryParam), true, acquireContext => {
var elements = DescribeElements(context);
var categoryDictionary = GetCategories();
var categoryDescriptorDictionary = new Dictionary<string, CategoryDescriptor>();

View File

@@ -5,17 +5,17 @@
}
<div class="item-properties actions">
<p>
@Html.ActionLink(T("{0} Properties", (string)Model.ContainerDisplayName).ToString(), "Edit", new { Area = "Contents", Id = (int)Model.ContainerId, ReturnUrl = Html.ViewContext.HttpContext.Request.RawUrl })
@Html.ActionLink(T("{0} Properties", (string)Model.ContainerDisplayName), "Edit", new { Area = "Contents", Id = (int)Model.ContainerId, ReturnUrl = Html.ViewContext.HttpContext.Request.RawUrl })
</p>
</div>
<div class="manage">
@if (itemContentTypes.Any()) {
foreach (var contentType in itemContentTypes) {
@Html.ActionLink(T("New {0}", contentType.DisplayName).ToString(), "Create", "Admin", new { area = "Contents", id = contentType.Name, containerId }, new { @class = "button primaryAction create-content" })
@Html.ActionLink(T("New {0}", contentType.DisplayName), "Create", "Admin", new { area = "Contents", id = contentType.Name, containerId }, new { @class = "button primaryAction create-content" })
}
}
else {
@Html.ActionLink(T("New Content").ToString(), "Create", "Admin", new { area = "Contents", containerId }, new { @class = "button primaryAction create-content" })
@Html.ActionLink(T("New Content"), "Create", "Admin", new { area = "Contents", containerId }, new { @class = "button primaryAction create-content" })
}
<a id="chooseItems" href="#" class="button primaryAction">@T("Choose Items")</a>
</div>

View File

@@ -58,7 +58,7 @@ namespace Orchard.MediaProcessing.Services {
}
private IDictionary<string, string> GetProfileCache(string profile) {
return _cacheManager.Get("MediaProcessing_" + profile, ctx => {
return _cacheManager.Get("MediaProcessing_" + profile, true, ctx => {
ctx.Monitor(_signals.When("MediaProcessing_Saved_" + profile));
var dictionary = new Dictionary<string, string>();

View File

@@ -32,7 +32,7 @@ namespace Orchard.MediaProcessing.Services {
public ImageProfilePart GetImageProfileByName(string name) {
var profileId = _cacheManager.Get("ProfileId_" + name, ctx => {
var profileId = _cacheManager.Get("ProfileId_" + name, true, ctx => {
var profile = _contentManager.Query<ImageProfilePart, ImageProfilePartRecord>()
.Where(x => x.Name == name)
.Slice(0, 1)

View File

@@ -463,7 +463,7 @@ namespace Orchard.OutputCache.Filters {
private CacheSettings CacheSettings {
get {
return _cacheSettings ?? (_cacheSettings = _cacheManager.Get(CacheSettings.CacheKey, context => {
return _cacheSettings ?? (_cacheSettings = _cacheManager.Get(CacheSettings.CacheKey, true, context => {
context.Monitor(_signals.When(CacheSettings.CacheKey));
return new CacheSettings(_workContext.CurrentSite.As<CacheSettingsPart>());
}));

View File

@@ -99,7 +99,7 @@ namespace Orchard.OutputCache.Services {
}
public IEnumerable<CacheRouteConfig> GetRouteConfigs() {
return _cacheManager.Get(RouteConfigsCacheKey,
return _cacheManager.Get(RouteConfigsCacheKey, true,
ctx => {
ctx.Monitor(_signals.When(RouteConfigsCacheKey));
return _repository.Fetch(c => true).Select(c => new CacheRouteConfig { RouteKey = c.RouteKey, Duration = c.Duration, GraceTime = c.GraceTime }).ToReadOnlyCollection();

View File

@@ -14,7 +14,7 @@ namespace Orchard.Scripting.Dlr.Services {
}
public object Evaluate(string expression, IEnumerable<IGlobalMethodProvider> providers) {
object execContextType = _cacheManager.Get("---", ctx => (object)_scriptingManager.ExecuteExpression(@"
object execContextType = _cacheManager.Get("---", true, ctx => (object)_scriptingManager.ExecuteExpression(@"
class ExecBlock
def initialize(callbacks)
@callbacks = callbacks
@@ -38,7 +38,7 @@ class ExecContext
end
ExecContext
"));
var ops = _cacheManager.Get("----", ctx => (ObjectOperations)_scriptingManager.ExecuteOperation(x => x));
var ops = _cacheManager.Get("----", true, ctx => (ObjectOperations)_scriptingManager.ExecuteOperation(x => x));
object execContext = _cacheManager.Get(expression, ctx => (object)ops.InvokeMember(execContextType, "alloc", expression));
dynamic result = ops.InvokeMember(execContext, "evaluate", new CallbackApi(this, providers));
return ConvertRubyValue(result);

View File

@@ -20,7 +20,7 @@ namespace Orchard.Scripting {
public Localizer T { get; set; }
public object Evaluate(string expression, IEnumerable<IGlobalMethodProvider> providers) {
var expr = _cacheManager.Get(expression, ctx => {
var expr = _cacheManager.Get(expression, true, ctx => {
var ast = ParseExpression(expression);
return new { Tree = ast, Errors = ast.GetErrors().ToList() };
});

View File

@@ -32,7 +32,7 @@ namespace Orchard.Tags.Services {
public IEnumerable<TagCount> GetPopularTags(int buckets, string slug) {
var cacheKey = "Orchard.Tags.TagCloud." + (slug ?? "") + '.' + buckets;
return _cacheManager.Get(cacheKey,
return _cacheManager.Get(cacheKey, true,
ctx => {
ctx.Monitor(_signals.When(TagCloudTagsChanged));
IEnumerable<TagCount> tagCounts;

View File

@@ -31,21 +31,33 @@ namespace Orchard.Taxonomies.Tokens {
context.For<TaxonomyField>("TaxonomyField")
.Token("Terms", field => String.Join(", ", field.Terms.Select(t => t.Name).ToArray()))
.Token(
token => {
var index = 0;
return (token.StartsWith("Terms:", StringComparison.OrdinalIgnoreCase) && int.TryParse(token.Substring("Terms:".Length), out index)) ? index.ToString() : null;
},
(token, t) => {
var index = Convert.ToInt32(token);
return index + 1 > t.Terms.Count() ? null : t.Terms.ElementAt(index).Name;
.Token(FilterTokenParam,
(index, field) => {
var term = field.Terms.ElementAtOrDefault(Convert.ToInt32(index));
return term != null ? term.Name : null;
})
// todo: extend Chain() in order to accept a filter like in Token() so that we can chain on an expression
.Chain("Terms:0", "Content", t => t.Terms.ElementAt(0))
.Chain("Terms:1", "Content", t => t.Terms.ElementAt(1))
.Chain("Terms:2", "Content", t => t.Terms.ElementAt(2))
.Chain("Terms:3", "Content", t => t.Terms.ElementAt(3))
.Chain(FilterChainParam, "Content", (index, field) => field.Terms.ElementAtOrDefault(Convert.ToInt32(index)))
;
}
private static string FilterTokenParam(string token) {
int index = 0;
return (token.StartsWith("Terms:", StringComparison.OrdinalIgnoreCase) && int.TryParse(token.Substring("Terms:".Length), out index)) ? index.ToString() : null;
}
private static Tuple<string, string> FilterChainParam(string token) {
int tokenLength = "Terms:".Length;
int index = 0;
int chainIndex = token.IndexOf('.');
if (!token.StartsWith("Terms:", StringComparison.OrdinalIgnoreCase) || chainIndex <= tokenLength)
return null;
if (int.TryParse(token.Substring(tokenLength, chainIndex - tokenLength), out index)) {
return new Tuple<string, string>(index.ToString(), token.Substring(chainIndex + 1));
}
else {
return null;
}
}
}
}

View File

@@ -55,7 +55,7 @@ namespace Orchard.Templates.Services {
}
private IDictionary<string, TemplateResult> BuildShapeProcessors() {
return _cacheManager.Get("Template.ShapeProcessors", ctx => {
return _cacheManager.Get("Template.ShapeProcessors", true, ctx => {
ctx.Monitor(_signals.When(DefaultTemplateService.TemplatesSignal));
// select all name of types which contains ShapePart

View File

@@ -7,5 +7,6 @@ namespace Orchard.Tokens {
public abstract EvaluateFor<TData> Chain(string token, string chainTarget, Func<TData, object> chainValue);
public abstract EvaluateFor<TData> Token(Func<string, TData, object> tokenValue);
public abstract EvaluateFor<TData> Token(Func<string, string> filter, Func<string, TData, object> tokenValue);
public abstract EvaluateFor<TData> Chain(Func<string, Tuple<string, string>> filter, string chainTarget, Func<string, TData, object> chainValue);
}
}

View File

@@ -153,6 +153,27 @@ namespace Orchard.Tokens.Implementation {
}
return this;
}
public override EvaluateFor<TData> Chain(Func<string, Tuple<string, string>> filter, string chainTarget, Func<string, TData, object> chainValue) {
var subTokens = _context.Tokens
.Where(kv => kv.Key.Contains('.'))
.Select(kv => {
var filterResult = filter(kv.Key);
return filterResult != null ? new Tuple<string, string, string>(filterResult.Item1, filterResult.Item2, kv.Value) : null;
})
.Where(st => st != null)
.ToList();
if (!subTokens.Any()) {
return this;
}
foreach(var chainGroup in subTokens.GroupBy(st => st.Item1)) {
var subValues = _context._manager.Evaluate(chainTarget, chainGroup.ToDictionary(cg => cg.Item2, cg => cg.Item3), new Dictionary<string, object> { { chainTarget, chainValue(chainGroup.Key, _data) } });
foreach (var subValue in subValues) {
_context.Values[subValue.Key] = subValue.Value;
}
}
return this;
}
}
private class EvaluateForSilent<TData> : EvaluateFor<TData> {
@@ -175,6 +196,10 @@ namespace Orchard.Tokens.Implementation {
public override EvaluateFor<TData> Chain(string token, string chainTarget, Func<TData, object> chainValue) {
return this;
}
public override EvaluateFor<TData> Chain(Func<string, Tuple<string, string>> filter, string chainTarget, Func<string, TData, object> chainValue) {
return this;
}
}
}

View File

@@ -36,7 +36,7 @@ namespace Orchard.Tokens.Providers {
.Token("DisplayUrl", T("Display Url"), T("Url to display the content."), "Url")
.Token("EditUrl", T("Edit Url"), T("Url to edit the content."), "Url")
.Token("Container", T("Container"), T("The container Content Item."), "Content")
.Token("Body", T("Body"), T("The body text of the content item."), "Content")
.Token("Body", T("Body"), T("The body text of the content item."), "Text")
;
// Token descriptors for fields

View File

@@ -13,38 +13,45 @@ namespace Orchard.Tokens.Providers {
public void Describe(DescribeContext context) {
context.For("Text", T("Text"), T("Tokens for text strings"))
.Token("Limit:*", T("Limit:<text length>[,<ellipsis>]"), T("Limit text to specified length and append an optional ellipsis text."))
.Token("Format:*", T("Format:<text format>"), T("Optional format specifier (e.g. foo{0}bar). See format strings at <a target=\"_blank\" href=\"http://msdn.microsoft.com/en-us/library/az4se3k1.aspx\">Standard Formats</a> and <a target=\"_blank\" href=\"http://msdn.microsoft.com/en-us/library/8kb3ddd4.aspx\">Custom Formats</a>"), "DateTime")
.Token("TrimEnd:*", T("TrimEnd:<chars|number>"), T("Trims the specified characters or number of them from the end of the string."), "Text")
.Token("Format:*", T("Format:<text format>"), T("Optional format specifier (e.g. foo{0}bar)."))
.Token("TrimEnd:*", T("TrimEnd:<chars|number>"), T("Trims the specified characters or number of them from the end of the string."))
.Token("UrlEncode", T("Url Encode"), T("Encodes a URL string."), "Text")
.Token("HtmlEncode", T("Html Encode"), T("Encodes an HTML string."), "Text")
.Token("LineEncode", T("Line Encode"), T("Replaces new lines with <br /> tags."))
.Token("JavaScriptEncode", T("JavaScript Encode"), T("Encodes a JavaScript string."), "Text")
.Token("LineEncode", T("Line Encode"), T("Replaces new lines with <br /> tags."), "Text")
;
}
public void Evaluate(EvaluateContext context) {
context.For<String>("Text", () => "")
.Token( // {Text}
// {Text}
.Token(
token => token == String.Empty ? String.Empty : null,
(token, d) => d.ToString())
.Token( // {Text.Limit:<length>[,<ellipsis>]}
token => {
if (token.StartsWith("Limit:", StringComparison.OrdinalIgnoreCase)) {
var param = token.Substring("Limit:".Length);
return param;
}
return null;
},
// {Text.Limit:<length>[,<ellipsis>]}
.Token(
token => FilterTokenParam("Limit:", token),
(token, t) => Limit(t, token))
// {Text.Format:<formatstring>}
.Token(
token => token.StartsWith("Format:", StringComparison.OrdinalIgnoreCase) ? token.Substring("Format:".Length) : null,
(token, d) => String.Format(d,token))
.Token(token => token.StartsWith("TrimEnd:", StringComparison.OrdinalIgnoreCase) ? token.Substring("TrimEnd:".Length) : null, TrimEnd)
token => FilterTokenParam("Format:", token),
(token, d) => String.Format(d, token))
// {Text.TrimEnd:<chars|number>}
.Token(token => FilterTokenParam("TrimEnd:", token), TrimEnd)
.Token("UrlEncode", HttpUtility.UrlEncode)
.Chain("UrlEncode", "Text", HttpUtility.UrlEncode)
.Token("HtmlEncode", HttpUtility.HtmlEncode)
.Chain("HtmlEncode", "Text", HttpUtility.HtmlEncode)
.Token("JavaScriptEncode", HttpUtility.JavaScriptStringEncode)
.Chain("JavaScriptEncode", "Text", HttpUtility.JavaScriptStringEncode)
.Token("LineEncode", text => text.Replace(System.Environment.NewLine, "<br />"))
.Chain("LineEncode", "Text", text => text.Replace(System.Environment.NewLine, "<br />"))
;
}
private static string FilterTokenParam(string tokenName, string token) {
return token.StartsWith(tokenName, StringComparison.OrdinalIgnoreCase) ? token.Substring(tokenName.Length) : null;
}
private static string TrimEnd(string token, string param) {

View File

@@ -1,4 +1,5 @@
using System;
using System.Linq;
using Orchard.ContentManagement;
using Orchard.Localization;
using Orchard.Security;
@@ -24,6 +25,9 @@ namespace Orchard.Tokens.Tests {
context.For("Date")
.Token("Now", T("Now"), T("Current system date in short date format. You can chain a .NET DateTime format string to customize."));
context.For("Users")
.Token("Users[*:]", T("Users"), T("A user by its username"), "User");
}
public void Evaluate(EvaluateContext context) {
@@ -36,6 +40,7 @@ namespace Orchard.Tokens.Tests {
context.For<IUser>("User", () => new TestUser { UserName = "CurrentUser" })
.Token("Name", u => u.UserName)
.Token("Email", u => u.Email)
.Token("Birthdate", u => "Nov 15")
.Chain("Birthdate", "DateTime", u => new DateTime(1978, 11, 15));
@@ -45,6 +50,29 @@ namespace Orchard.Tokens.Tests {
context.For<DateTime>("DateTime")
.Token((token, value) => value.ToString(token));
context.For<TestUser[]>("Users", () => new TestUser[] {
new TestUser { UserName = "User1", Email = "user1@test.com" },
new TestUser { UserName = "User2", Email = "user2@test.com" },
new TestUser { UserName = "User3", Email = "user3@test.com" }
})
.Token(
(token) => token.StartsWith("User:", StringComparison.OrdinalIgnoreCase) ? token.Substring("User:".Length) : null,
(userName, users) => users.Where(u => u.UserName == userName).Select(u => u.UserName).FirstOrDefault()
)
.Chain(
(token) => {
int tokenLength = "User:".Length;
int chainIndex = token.IndexOf('.');
if (token.StartsWith("User:", StringComparison.OrdinalIgnoreCase) && chainIndex > tokenLength)
return new Tuple<string, string>(token.Substring(tokenLength, chainIndex - tokenLength), token.Substring(chainIndex + 1));
else
return null;
},
"User",
(userName, users) => users.Where(u => u.UserName == userName).FirstOrDefault()
);
}
}

View File

@@ -29,10 +29,11 @@ namespace Orchard.Tokens.Tests {
[Test]
public void TestDescribe() {
var allTokens = _tokenManager.Describe(null);
Assert.That(allTokens.Count(), Is.EqualTo(3));
Assert.That(allTokens.Count(), Is.EqualTo(4));
Assert.That(allTokens.Any(d => d.Target == "Site"));
Assert.That(allTokens.Any(d => d.Target == "User"));
Assert.That(allTokens.Any(d => d.Target == "Date"));
Assert.That(allTokens.Any(d => d.Target == "Users"));
var tokens = allTokens.Single(d => d.Target == "Site").Tokens;
Assert.That(string.Join(",", tokens.Select(td => td.Target)), Is.EqualTo("Site,Site,Site,Site"));
@@ -59,7 +60,7 @@ namespace Orchard.Tokens.Tests {
[Test]
public void TestDescribeFilter() {
var tokenDescriptors = _tokenManager.Describe(null);
Assert.That(tokenDescriptors.Count(), Is.EqualTo(3));
Assert.That(tokenDescriptors.Count(), Is.EqualTo(4));
tokenDescriptors = _tokenManager.Describe(new[] { "Site" });
Assert.That(tokenDescriptors.Count(), Is.EqualTo(1));
Assert.That(tokenDescriptors.First().Target, Is.EqualTo("Site"));

View File

@@ -40,6 +40,18 @@ namespace Orchard.Tokens.Tests {
Assert.That(_tokenizer.Replace("{Site.CurrentUser.Birthdate.yyyy}", null), Is.EqualTo("1978"));
}
[Test]
public void TestParameterizedTokens() {
Assert.That(_tokenizer.Replace("{Users.User:User2}", null), Is.EqualTo("User2"));
Assert.That(_tokenizer.Replace("{Users.User:FakeUser}", null), Is.EqualTo(""));
}
[Test]
public void TestParameterizedChainedTokens() {
Assert.That(_tokenizer.Replace("{Users.User:User2.Email}", null), Is.EqualTo("user2@test.com"));
Assert.That(_tokenizer.Replace("{Users.User:FakeUser.Email}", null), Is.EqualTo(""));
}
[Test]
public void TestMissingTokens() {
Assert.That(_tokenizer.Replace("[{Site.NotAToken}]", null), Is.EqualTo("[]"));

View File

@@ -255,14 +255,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Layouts", "Orchard.
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.DynamicForms", "Orchard.Web\Modules\Orchard.DynamicForms\Orchard.DynamicForms.csproj", "{82190F52-2901-46D6-8A4C-34649959483F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Dashboards", "Orchard.Web\Modules\Orchard.Dashboards\Orchard.Dashboards.csproj", "{BAC82DB9-F4C4-4DD1-ABDB-F70E6229E6B0}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{DF3909B0-1DDD-4D8A-9919-56FC438E25E2}"
ProjectSection(SolutionItems) = preProject
Rebracer.xml = Rebracer.xml
WebEssentials-Settings.json = WebEssentials-Settings.json
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Dashboards", "Orchard.Web\Modules\Orchard.Dashboards\Orchard.Dashboards.csproj", "{BAC82DB9-F4C4-4DD1-ABDB-F70E6229E6B0}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Gulp", "Gulp", "{90EBEE36-B5CD-42A8-A21B-76270E2C5D24}"
ProjectSection(SolutionItems) = preProject
Gulpfile.js = Gulpfile.js

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Orchard.Logging;
using Orchard.Exceptions;
namespace Orchard.Caching {
public class DefaultAsyncTokenProvider : IAsyncTokenProvider {
@@ -37,9 +38,12 @@ namespace Orchard.Caching {
try {
_task(token => _taskTokens.Add(token));
}
catch (Exception e) {
Logger.Error(e, "Error while monitoring extension files. Assuming extensions are not current.");
_taskException = e;
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
Logger.Error(ex, "Error while monitoring extension files. Assuming extensions are not current.");
_taskException = ex;
}
finally {
_isTaskFinished = true;

View File

@@ -5,4 +5,17 @@ namespace Orchard.Caching {
TResult Get<TKey, TResult>(TKey key, Func<AcquireContext<TKey>, TResult> acquire);
ICache<TKey, TResult> GetCache<TKey, TResult>();
}
public static class CacheManagerExtensions {
public static TResult Get<TKey, TResult>(this ICacheManager cacheManager, TKey key, bool lazy, Func<AcquireContext<TKey>, TResult> acquire) {
// Wrap the call in a Lazy initializer to prevent multiple processes from
// executing the same lambda in parallel.
if (lazy) {
return cacheManager.Get<TKey, Lazy<TResult>>(key, k => new Lazy<TResult>(() => acquire(k))).Value;
}
else {
return cacheManager.Get(key, acquire);
}
}
}
}

View File

@@ -15,6 +15,7 @@ using Orchard.FileSystems.VirtualPath;
using Orchard.Localization;
using Orchard.Logging;
using Orchard.Tasks;
using Orchard.Exceptions;
namespace Orchard.Commands {
@@ -94,19 +95,21 @@ namespace Orchard.Commands {
return CommandReturnCodes.Ok;
}
catch (OrchardCommandHostRetryException e) {
catch (OrchardCommandHostRetryException ex) {
// Special "Retry" return code for our host
output.WriteLine(T("{0} (Retrying...)", e.Message));
output.WriteLine(T("{0} (Retrying...)", ex.Message));
return CommandReturnCodes.Retry;
}
catch (Exception e) {
if (e is TargetInvocationException &&
e.InnerException != null) {
// If this is an exception coming from reflection and there is an innerexception which is the actual one, redirect
e = e.InnerException;
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
OutputException(output, T("Error executing command \"{0}\"", string.Join(" ", args)), e);
if (ex is TargetInvocationException &&
ex.InnerException != null) {
// If this is an exception coming from reflection and there is an innerexception which is the actual one, redirect
ex = ex.InnerException;
}
OutputException(output, T("Error executing command \"{0}\"", string.Join(" ", args)), ex);
return CommandReturnCodes.Fail;
}
}
@@ -116,13 +119,16 @@ namespace Orchard.Commands {
_hostContainer = CreateHostContainer();
return CommandReturnCodes.Ok;
}
catch (OrchardCommandHostRetryException e) {
catch (OrchardCommandHostRetryException ex) {
// Special "Retry" return code for our host
output.WriteLine(T("{0} (Retrying...)", e.Message));
output.WriteLine(T("{0} (Retrying...)", ex.Message));
return CommandReturnCodes.Retry;
}
catch (Exception e) {
OutputException(output, T("Error starting up Orchard command line host"), e);
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
OutputException(output, T("Error starting up Orchard command line host"), ex);
return CommandReturnCodes.Fail;
}
}
@@ -135,8 +141,11 @@ namespace Orchard.Commands {
}
return CommandReturnCodes.Ok;
}
catch (Exception e) {
OutputException(output, T("Error shutting down Orchard command line host"), e);
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
OutputException(output, T("Error shutting down Orchard command line host"), ex);
return CommandReturnCodes.Fail;
}
}

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Orchard.Localization;
using Orchard.Exceptions;
namespace Orchard.Commands {
public abstract class DefaultOrchardCommandHandler : ICommandHandler {
@@ -41,12 +42,15 @@ namespace Orchard.Commands {
object value = Convert.ChangeType(commandSwitch.Value, propertyInfo.PropertyType);
propertyInfo.SetValue(this, value, null/*index*/);
}
catch(Exception e) {
catch(Exception ex) {
if (ex.IsFatal()) {
throw;
}
string message = T("Error converting value \"{0}\" to \"{1}\" for switch \"{2}\"",
LocalizedString.TextOrDefault(commandSwitch.Value, T("(empty)")),
propertyInfo.PropertyType.FullName,
commandSwitch.Key).Text;
throw new InvalidOperationException(message, e);
throw new InvalidOperationException(message, ex);
}
}

View File

@@ -817,7 +817,7 @@ namespace Orchard.ContentManagement {
}
private ContentTypeRecord AcquireContentTypeRecord(string contentType) {
var contentTypeId = _cacheManager.Get(contentType + "_Record", ctx => {
var contentTypeId = _cacheManager.Get(contentType + "_Record", true, ctx => {
ctx.Monitor(_signals.When(contentType + "_Record"));
var contentTypeRecord = _contentTypeRepository.Get(x => x.Name == contentType);

View File

@@ -76,7 +76,7 @@ namespace Orchard.ContentManagement {
}
private int GetContentTypeRecordId(string contentType) {
return _cacheManager.Get(contentType + "_Record", ctx => {
return _cacheManager.Get(contentType + "_Record", true, ctx => {
ctx.Monitor(_signals.When(contentType + "_Record"));
var contentTypeRecord = _contentTypeRepository.Get(x => x.Name == contentType);

View File

@@ -1,9 +1,13 @@
using System;
using System.Linq;
using Orchard.Data.Migration.Interpreters;
using Orchard.Data.Migration.Schema;
using Orchard.Environment;
using Orchard.Environment.Configuration;
using Orchard.Environment.Features;
using Orchard.Logging;
using Orchard.Tasks.Locking.Services;
using Orchard.Exceptions;
namespace Orchard.Data.Migration {
/// <summary>
@@ -13,24 +17,35 @@ namespace Orchard.Data.Migration {
private readonly IDataMigrationManager _dataMigrationManager;
private readonly IFeatureManager _featureManager;
private readonly IDistributedLockService _distributedLockService;
private readonly IDataMigrationInterpreter _dataMigrationInterpreter;
private readonly ShellSettings _shellSettings;
private readonly ITransactionManager _transactionManager;
public AutomaticDataMigrations(
IDataMigrationManager dataMigrationManager,
IDataMigrationInterpreter dataMigrationInterpreter,
IFeatureManager featureManager,
IDistributedLockService distributedLockService) {
IDistributedLockService distributedLockService,
ITransactionManager transactionManager,
ShellSettings shellSettings) {
_dataMigrationManager = dataMigrationManager;
_featureManager = featureManager;
_distributedLockService = distributedLockService;
_shellSettings = shellSettings;
_transactionManager = transactionManager;
_dataMigrationInterpreter = dataMigrationInterpreter;
Logger = NullLogger.Instance;
}
public ILogger Logger { get; set; }
public ILogger Logger { get; set; }
public void Activated() {
EnsureDistributedLockSchemaExists();
IDistributedLock @lock;
if(_distributedLockService.TryAcquireLock(GetType().FullName, TimeSpan.FromMinutes(30), TimeSpan.FromMilliseconds(250), out @lock)) {
if (_distributedLockService.TryAcquireLock(GetType().FullName, TimeSpan.FromMinutes(30), TimeSpan.FromMilliseconds(250), out @lock)) {
using (@lock) {
// Let's make sure that the basic set of features is enabled. If there are any that are not enabled, then let's enable them first.
var theseFeaturesShouldAlwaysBeActive = new[] {
@@ -48,7 +63,10 @@ namespace Orchard.Data.Migration {
_dataMigrationManager.Update(feature);
}
catch (Exception ex) {
Logger.Error(ex, "Could not run migrations automatically on {0}.", feature);
if (ex.IsFatal()) {
throw;
}
Logger.Error("Could not run migrations automatically on " + feature, ex);
}
}
}
@@ -58,5 +76,16 @@ namespace Orchard.Data.Migration {
public void Terminating() {
// No-op.
}
/// <summary>
/// This ensures that the framework migrations have run for the distributed locking feature, as existing Orchard installations will not have the required tables when upgrading.
/// </summary>
private void EnsureDistributedLockSchemaExists() {
// Ensure the distributed lock record schema exists.
var schemaBuilder = new SchemaBuilder(_dataMigrationInterpreter);
var distributedLockSchemaBuilder = new DistributedLockSchemaBuilder(_shellSettings, schemaBuilder);
if (distributedLockSchemaBuilder.EnsureSchema())
_transactionManager.RequireNew();
}
}
}

View File

@@ -9,6 +9,7 @@ using Orchard.Data.Migration.Schema;
using Orchard.Environment.Extensions;
using Orchard.Localization;
using Orchard.Logging;
using Orchard.Exceptions;
namespace Orchard.Data.Migration {
/// <summary>
@@ -123,6 +124,9 @@ namespace Orchard.Data.Migration {
current = (int)lookupTable[current].Invoke(migration, new object[0]);
}
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
Logger.Error(ex, "An unexpected error occurred while applying migration on {0} from version {1}.", feature, current);
throw;
}
@@ -139,10 +143,13 @@ namespace Orchard.Data.Migration {
dataMigrationRecord.Version = current;
}
}
catch (Exception e) {
Logger.Error(e, "Error while running migration version {0} for {1}.", current, feature);
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
Logger.Error(ex, "Error while running migration version {0} for {1}.", current, feature);
_transactionManager.Cancel();
throw new OrchardException(T("Error while running migration version {0} for {1}.", current, feature), e);
throw new OrchardException(T("Error while running migration version {0} for {1}.", current, feature), ex);
}
}

View File

@@ -1,6 +1,7 @@
using System;
using Orchard.Data.Migration.Interpreters;
using Orchard.Localization;
using Orchard.Exceptions;
namespace Orchard.Data.Migration.Schema {
public class SchemaBuilder {
@@ -72,6 +73,9 @@ namespace Orchard.Data.Migration.Schema {
Run(sqlStatmentCommand);
return this;
} catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
throw new OrchardException(T("An unexpected error occured while executing the SQL statement: {0}", sql), ex); // Add the sql to the nested exception information
}
}

View File

@@ -11,6 +11,7 @@ using Orchard.Environment.ShellBuilders.Models;
using Orchard.FileSystems.AppData;
using Orchard.Logging;
using Orchard.Utility;
using Orchard.Exceptions;
namespace Orchard.Data {
public class SessionConfigurationCache : ISessionConfigurationCache {
@@ -80,11 +81,11 @@ namespace Orchard.Data {
formatter.Serialize(stream, cache.Configuration);
}
}
catch (SerializationException e) {
catch (SerializationException ex) {
//Note: This can happen when multiple processes/AppDomains try to save
// the cached configuration at the same time. Only one concurrent
// writer will win, and it's harmless for the other ones to fail.
for (Exception scan = e; scan != null; scan = scan.InnerException)
for (Exception scan = ex; scan != null; scan = scan.InnerException)
Logger.Warning("Error storing new NHibernate cache configuration: {0}", scan.Message);
}
}
@@ -118,8 +119,11 @@ namespace Orchard.Data {
};
}
}
catch (Exception e) {
for (var scan = e; scan != null; scan = scan.InnerException)
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
for (var scan = ex; scan != null; scan = scan.InnerException)
Logger.Warning("Error reading the cached NHibernate configuration: {0}", scan.Message);
Logger.Information("A new one will be re-generated.");
return null;

View File

@@ -37,7 +37,7 @@ namespace Orchard.DisplayManagement.Descriptors {
public ILogger Logger { get; set; }
public ShapeTable GetShapeTable(string themeName) {
return _cacheManager.Get(themeName ?? "", x => {
return _cacheManager.Get(themeName ?? "", true, x => {
Logger.Information("Start building shape table");
var alterationSets = _parallelCacheContext.RunInParallel(_bindingStrategies, bindingStrategy => {

View File

@@ -30,7 +30,7 @@ namespace Orchard.DisplayManagement.Descriptors.ShapePlacementStrategy {
public bool DisableMonitoring { get; set; }
public PlacementFile Parse(string virtualPath) {
return _cacheManager.Get(virtualPath, context => {
return _cacheManager.Get(virtualPath, true, context => {
if (!DisableMonitoring) {
Logger.Debug("Monitoring virtual path \"{0}\"", virtualPath);

View File

@@ -77,7 +77,7 @@ namespace Orchard.DisplayManagement.Descriptors.ShapeTemplateStrategy {
var pathContexts = harvesterInfos.SelectMany(harvesterInfo => harvesterInfo.subPaths.Select(subPath => {
var basePath = Path.Combine(extensionDescriptor.Location, extensionDescriptor.Id).Replace(Path.DirectorySeparatorChar, '/');
var virtualPath = Path.Combine(basePath, subPath).Replace(Path.DirectorySeparatorChar, '/');
var fileNames = _cacheManager.Get(virtualPath, ctx => {
var fileNames = _cacheManager.Get(virtualPath, true, ctx => {
if (!_virtualPathProvider.DirectoryExists(virtualPath))
return new List<string>();

View File

@@ -14,6 +14,7 @@ using Orchard.Logging;
using Orchard.Mvc;
using Orchard.Mvc.Extensions;
using Orchard.Utility.Extensions;
using Orchard.Exceptions;
namespace Orchard.Environment {
// All the event handlers that DefaultOrchardHost implements have to be declared in OrchardStarter.
@@ -145,8 +146,11 @@ namespace Orchard.Environment {
var context = CreateShellContext(settings, StandaloneEnvironmentOptions.None);
ActivateShell(context);
}
catch (Exception e) {
Logger.Error(e, "A tenant could not be started: " + settings.Name);
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
Logger.Error(ex, "A tenant could not be started: " + settings.Name);
}
while (_processingEngine.AreTasksPending()) {
Logger.Debug("Processing pending task after activate Shell");

View File

@@ -11,6 +11,7 @@ using Orchard.Owin;
using Orchard.Tasks;
using Orchard.UI;
using Orchard.WebApi.Routes;
using Orchard.Exceptions;
using IModelBinderProvider = Orchard.Mvc.ModelBinders.IModelBinderProvider;
namespace Orchard.Environment {
@@ -98,8 +99,12 @@ namespace Orchard.Environment {
try {
action();
}
catch(Exception e) {
Logger.Error(e, "An unexcepted error occured while terminating the Shell");
catch(Exception ex) {
if (ex.IsFatal()) {
throw;
}
Logger.Error(ex, "An unexcepted error occured while terminating the Shell");
}
}
}

View File

@@ -8,6 +8,7 @@ using Orchard.FileSystems.Dependencies;
using Orchard.FileSystems.VirtualPath;
using Orchard.Localization;
using Orchard.Logging;
using Orchard.Exceptions;
namespace Orchard.Environment.Extensions.Compilers {
/// <summary>
@@ -106,10 +107,13 @@ namespace Orchard.Environment.Extensions.Compilers {
}
}
}
catch (Exception e) {
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
//Note: we need to embed the "e.Message" in the exception text because
// ASP.NET build manager "swallows" inner exceptions from this method.
throw new OrchardCoreException(T("Error compiling module \"{0}\" from file \"{1}\":\r\n{2}", moduleName, context.VirtualPath, e.Message), e);
throw new OrchardCoreException(T("Error compiling module \"{0}\" from file \"{1}\":\r\n{2}", moduleName, context.VirtualPath, ex.Message), ex);
}
}

View File

@@ -23,7 +23,7 @@ namespace Orchard.Environment.Extensions.Compilers {
public bool DisableMonitoring { get; set; }
public ProjectFileDescriptor Parse(string virtualPath) {
return _cacheManager.Get(virtualPath,
return _cacheManager.Get(virtualPath, true,
ctx => {
if (!DisableMonitoring) {
Logger.Debug("Monitoring virtual path \"{0}\"", virtualPath);

View File

@@ -9,6 +9,7 @@ using Orchard.Localization;
using Orchard.Logging;
using Orchard.Utility;
using Orchard.Utility.Extensions;
using Orchard.Exceptions;
namespace Orchard.Environment.Extensions {
public class ExtensionManager : IExtensionManager {
@@ -44,7 +45,7 @@ namespace Orchard.Environment.Extensions {
}
public IEnumerable<ExtensionDescriptor> AvailableExtensions() {
return _cacheManager.Get("AvailableExtensions", ctx =>
return _cacheManager.Get("AvailableExtensions", true, ctx =>
_parallelCacheContext
.RunInParallel(_folders, folder => folder.AvailableExtensions().ToList())
.SelectMany(descriptors => descriptors)
@@ -52,7 +53,7 @@ namespace Orchard.Environment.Extensions {
}
public IEnumerable<FeatureDescriptor> AvailableFeatures() {
return _cacheManager.Get("AvailableFeatures", ctx =>
return _cacheManager.Get("AvailableFeatures", true, ctx =>
AvailableExtensions()
.SelectMany(ext => ext.Features)
.OrderByDependenciesAndPriorities(HasDependency, GetPriority)
@@ -92,7 +93,7 @@ namespace Orchard.Environment.Extensions {
var result =
_parallelCacheContext
.RunInParallel(featureDescriptors, descriptor => _cacheManager.Get(descriptor.Id, ctx => LoadFeature(descriptor)))
.RunInParallel(featureDescriptors, descriptor => _cacheManager.Get(descriptor.Id, true, ctx => LoadFeature(descriptor)))
.ToArray();
Logger.Information("Done loading features");
@@ -106,7 +107,7 @@ namespace Orchard.Environment.Extensions {
ExtensionEntry extensionEntry;
try {
extensionEntry = _cacheManager.Get(extensionId, ctx => {
extensionEntry = _cacheManager.Get(extensionId, true, ctx => {
var entry = BuildEntry(extensionDescriptor);
if (entry != null) {
ctx.Monitor(_asyncTokenProvider.GetToken(monitor => {
@@ -119,6 +120,9 @@ namespace Orchard.Environment.Extensions {
});
}
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
Logger.Error(ex, "Error loading extension '{0}'", extensionId);
throw new OrchardException(T("Error while loading extension '{0}'.", extensionId), ex);
}

View File

@@ -9,6 +9,7 @@ using Orchard.FileSystems.WebSite;
using Orchard.Localization;
using Orchard.Logging;
using Orchard.Utility.Extensions;
using Orchard.Exceptions;
namespace Orchard.Environment.Extensions.Folders {
public class ExtensionHarvester : IExtensionHarvester {
@@ -57,7 +58,7 @@ namespace Orchard.Environment.Extensions.Folders {
private IEnumerable<ExtensionDescriptor> HarvestExtensions(string path, string extensionType, string manifestName, bool manifestIsOptional) {
string key = string.Format("{0}-{1}-{2}", path, manifestName, extensionType);
return _cacheManager.Get(key, ctx => {
return _cacheManager.Get(key, true, ctx => {
if (!DisableMonitoring) {
Logger.Debug("Monitoring virtual path \"{0}\"", path);
ctx.Monitor(_webSiteFolder.WhenPathChanges(path));
@@ -100,6 +101,9 @@ namespace Orchard.Environment.Extensions.Folders {
}
catch (Exception ex) {
// Ignore invalid module manifests
if (ex.IsFatal()) {
throw;
}
Logger.Error(ex, "The module '{0}' could not be loaded. It was ignored.", extensionId);
_criticalErrorProvider.RegisterErrorMessage(T("The extension '{0}' manifest could not be loaded. It was ignored.", extensionId));
}
@@ -134,7 +138,7 @@ namespace Orchard.Environment.Extensions.Folders {
}
private ExtensionDescriptor GetExtensionDescriptor(string locationPath, string extensionId, string extensionType, string manifestPath, bool manifestIsOptional) {
return _cacheManager.Get(manifestPath, context => {
return _cacheManager.Get(manifestPath, true, context => {
if (!DisableMonitoring) {
Logger.Debug("Monitoring virtual path \"{0}\"", manifestPath);
context.Monitor(_webSiteFolder.WhenPathChanges(manifestPath));

View File

@@ -4,6 +4,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Orchard.Logging;
using Orchard.Exceptions;
namespace Orchard.Environment {
public interface IAssemblyLoader {
@@ -25,8 +27,11 @@ namespace Orchard.Environment {
try {
return _loadedAssemblies.GetOrAdd(this.ExtractAssemblyShortName(assemblyName), shortName => LoadWorker(shortName, assemblyName));
}
catch (Exception e) {
Logger.Error(e, "Error loading assembly '{0}'", assemblyName);
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
Logger.Error(ex, "Error loading assembly '{0}'", assemblyName);
return null;
}
}

View File

@@ -37,7 +37,7 @@ namespace Orchard.Environment {
public string Resolve(string shortName) {
// A few common .net framework assemblies are referenced by the Orchard.Framework assembly.
// Look into those to see if we can find the assembly we are looking for.
var orchardFrameworkReferences = _cacheManager.Get(typeof(IAssemblyLoader), ctx =>
var orchardFrameworkReferences = _cacheManager.Get(typeof(IAssemblyLoader), true, ctx =>
ctx.Key.Assembly
.GetReferencedAssemblies()
.GroupBy(n => AssemblyLoaderExtensions.ExtractAssemblyShortName(n.FullName), StringComparer.OrdinalIgnoreCase)

View File

@@ -53,8 +53,9 @@ namespace Orchard.Environment {
return BuildManager.GetCompiledAssembly(virtualPath);
}
catch (Exception ex) {
if (ex.IsFatal()) throw;
if (ex.IsFatal()) {
throw;
}
Logger.Warning(ex, "Error when compiling assembly under {0}.", virtualPath);
return null;
}

View File

@@ -5,6 +5,7 @@ using Orchard.Environment.Descriptor;
using Orchard.Environment.Descriptor.Models;
using Orchard.FileSystems.AppData;
using Orchard.Logging;
using Orchard.Exceptions;
namespace Orchard.Environment {
public interface IHostLocalRestart {
@@ -45,8 +46,11 @@ namespace Orchard.Environment {
try {
_appDataFolder.CreateFile(fileName, "Host Restart");
}
catch(Exception e) {
Logger.Warning(e, "Error updating file '{0}'", fileName);
catch(Exception ex) {
if (ex.IsFatal()) {
throw;
}
Logger.Warning(ex, "Error updating file '{0}'", fileName);
}
}
}

View File

@@ -6,6 +6,7 @@ using System.Timers;
using System.Web.Compilation;
using Orchard.FileSystems.VirtualPath;
using Orchard.Logging;
using Orchard.Exceptions;
namespace Orchard.Environment {
public interface IViewsBackgroundCompilation {
@@ -136,10 +137,13 @@ namespace Orchard.Environment {
if (firstFile != null)
BuildManager.GetCompiledAssembly(firstFile);
}
catch(Exception e) {
catch(Exception ex) {
if (ex.IsFatal()) {
throw;
}
// Some views might not compile, this is ok and harmless in this
// context of pre-compiling views.
Logger.Information(e, "Compilation of directory '{0}' skipped", viewDirectory);
Logger.Information(ex, "Compilation of directory '{0}' skipped", viewDirectory);
}
stopwatch.Stop();
Logger.Information("Directory '{0}' compiled in {1} msec", viewDirectory, stopwatch.ElapsedMilliseconds);

View File

@@ -53,6 +53,9 @@ namespace Orchard.Events {
return TryInvoke(eventHandler, messageName, interfaceName, methodName, eventData, out returnValue);
}
catch (Exception exception) {
if (exception.IsFatal()) {
throw;
}
if (!_exceptionPolicy.HandleException(this, exception)) {
throw;
}

View File

@@ -7,6 +7,7 @@ using Orchard.FileSystems.VirtualPath;
using Orchard.Localization;
using Orchard.Logging;
using Orchard.Validation;
using Orchard.Exceptions;
namespace Orchard.FileSystems.AppData {
public class AppDataFolder : IAppDataFolder {
@@ -78,8 +79,11 @@ namespace Orchard.FileSystems.AppData {
try {
File.Delete(destinationFileName);
}
catch (Exception e) {
throw new OrchardCoreException(T("Unable to make room for file \"{0}\" in \"App_Data\" folder", destinationFileName), e);
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
throw new OrchardCoreException(T("Unable to make room for file \"{0}\" in \"App_Data\" folder", destinationFileName), ex);
}
}

View File

@@ -34,7 +34,7 @@ namespace Orchard.FileSystems.Dependencies {
}
public IEnumerable<DependencyDescriptor> LoadDescriptors() {
return _cacheManager.Get(PersistencePath,
return _cacheManager.Get(PersistencePath, true,
ctx => {
_appDataFolder.CreateDirectory(BasePath);

View File

@@ -5,6 +5,7 @@ using System.Xml.Linq;
using Orchard.Caching;
using Orchard.FileSystems.AppData;
using Orchard.Logging;
using Orchard.Exceptions;
namespace Orchard.FileSystems.Dependencies {
/// <summary>
@@ -64,7 +65,7 @@ namespace Orchard.FileSystems.Dependencies {
}
public IEnumerable<ActivatedExtensionDescriptor> LoadDescriptors() {
return _cacheManager.Get(PersistencePath, ctx => {
return _cacheManager.Get(PersistencePath, true, ctx => {
_appDataFolder.CreateDirectory(BasePath);
if (!DisableMonitoring) {
@@ -135,8 +136,11 @@ namespace Orchard.FileSystems.Dependencies {
return XDocument.Load(stream);
}
}
catch (Exception e) {
Logger.Information(e, "Error reading file '{0}'. Assuming empty.", persistancePath);
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
Logger.Information(ex, "Error reading file '{0}'. Assuming empty.", persistancePath);
return new XDocument();
}
}

View File

@@ -6,6 +6,7 @@ using System.Web.Hosting;
using Orchard.Environment.Configuration;
using Orchard.Localization;
using Orchard.Validation;
using Orchard.Exceptions;
namespace Orchard.FileSystems.Media {
public class FileSystemStorageProvider : IStorageProvider {
@@ -155,6 +156,9 @@ namespace Orchard.FileSystems.Media {
directoryInfo.Create();
}
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
throw new ArgumentException(T("The folder could not be created at path: {0}. {1}", path, ex).ToString());
}
}

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Web;
using System.Web.Hosting;
using Orchard.Logging;
using Orchard.Exceptions;
namespace Orchard.FileSystems.VirtualPath {
public class DefaultVirtualPathProvider : IVirtualPathProvider {
@@ -59,9 +60,12 @@ namespace Orchard.FileSystems.VirtualPath {
}
return result;
}
catch (Exception e) {
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
// The initial path might have been invalid (e.g. path indicates a path outside the application root)
Logger.Information(e, "Path '{0}' cannot be made app relative", virtualPath);
Logger.Information(ex, "Path '{0}' cannot be made app relative", virtualPath);
return null;
}
}
@@ -156,8 +160,11 @@ namespace Orchard.FileSystems.VirtualPath {
try {
return FileExists(virtualPath);
}
catch (Exception e) {
Logger.Information(e, "File '{0}' can not be checked for existence. Assuming doesn't exist.", virtualPath);
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
Logger.Information(ex, "File '{0}' can not be checked for existence. Assuming doesn't exist.", virtualPath);
return false;
}
}

View File

@@ -26,7 +26,7 @@ namespace Orchard.Localization.Services {
}
public IEnumerable<string> ListCultures() {
return _cacheManager.Get("Cultures", context => {
return _cacheManager.Get("Cultures", true, context => {
context.Monitor(_signals.When("culturesChanged"));
return _cultureRepository.Table.Select(o => o.Culture).ToList();

View File

@@ -90,7 +90,7 @@ namespace Orchard.Localization.Services {
// Cache entry will be invalidated any time the directories hosting
// the .po files are modified.
private CultureDictionary LoadCulture(string culture) {
return _cacheManager.Get(culture, ctx => {
return _cacheManager.Get(culture, true, ctx => {
ctx.Monitor(_signals.When("culturesChanged"));
return new CultureDictionary {
CultureName = culture,

View File

@@ -5,6 +5,7 @@ using Orchard.Logging;
using Orchard.Messaging.Events;
using Orchard.Messaging.Models;
using Orchard.ContentManagement.Records;
using Orchard.Exceptions;
namespace Orchard.Messaging.Services {
[Obsolete]
@@ -40,8 +41,11 @@ namespace Orchard.Messaging.Services {
PrepareAndSend(type, properties, context);
}
catch ( Exception e ) {
Logger.Error(e, "An error occured while sending the message {0}", type);
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
Logger.Error(ex, "An error occured while sending the message {0}", type);
}
}
@@ -60,8 +64,11 @@ namespace Orchard.Messaging.Services {
PrepareAndSend(type, properties, context);
}
catch (Exception e) {
Logger.Error(e, "An error occured while sending the message {0}", type);
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
Logger.Error(ex, "An error occured while sending the message {0}", type);
}
}

View File

@@ -0,0 +1,144 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.WebPages;
namespace Orchard.Mvc.Html {
public static class LinkExtensions {
public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, IHtmlString linkText, string actionName) {
return ActionLink(htmlHelper, linkText, actionName, null /* controllerName */, new RouteValueDictionary(), new RouteValueDictionary());
}
public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, IHtmlString linkText, string actionName, object routeValues) {
return ActionLink(htmlHelper, linkText, actionName, null /* controllerName */, ObjectToDictionary(routeValues), new RouteValueDictionary());
}
public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, IHtmlString linkText, string actionName, object routeValues, object htmlAttributes) {
return ActionLink(htmlHelper, linkText, actionName, null /* controllerName */, ObjectToDictionary(routeValues), HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, IHtmlString linkText, string actionName, RouteValueDictionary routeValues) {
return ActionLink(htmlHelper, linkText, actionName, null /* controllerName */, routeValues, new RouteValueDictionary());
}
public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, IHtmlString linkText, string actionName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
return ActionLink(htmlHelper, linkText, actionName, null /* controllerName */, routeValues, htmlAttributes);
}
public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, IHtmlString linkText, string actionName, string controllerName) {
return ActionLink(htmlHelper, linkText, actionName, controllerName, new RouteValueDictionary(), new RouteValueDictionary());
}
public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, IHtmlString linkText, string actionName, string controllerName, object routeValues, object htmlAttributes) {
return ActionLink(htmlHelper, linkText, actionName, controllerName, ObjectToDictionary(routeValues), HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, IHtmlString linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
if (String.IsNullOrEmpty(linkText.ToString())) {
throw new ArgumentException("Argument must be a non empty string", "linkText");
}
return MvcHtmlString.Create(GenerateLink(htmlHelper.ViewContext.RequestContext, htmlHelper.RouteCollection, linkText, null /* routeName */, actionName, controllerName, routeValues, htmlAttributes));
}
public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, IHtmlString linkText, string actionName, string controllerName, string protocol, string hostName, string fragment, object routeValues, object htmlAttributes) {
return ActionLink(htmlHelper, linkText, actionName, controllerName, protocol, hostName, fragment, ObjectToDictionary(routeValues), HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, IHtmlString linkText, string actionName, string controllerName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
if (String.IsNullOrEmpty(linkText.ToString())) {
throw new ArgumentException("Argument must be a non empty string", "linkText");
}
return MvcHtmlString.Create(GenerateLink(htmlHelper.ViewContext.RequestContext, htmlHelper.RouteCollection, linkText, null /* routeName */, actionName, controllerName, protocol, hostName, fragment, routeValues, htmlAttributes));
}
public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, IHtmlString linkText, object routeValues) {
return RouteLink(htmlHelper, linkText, ObjectToDictionary(routeValues));
}
public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, IHtmlString linkText, RouteValueDictionary routeValues) {
return RouteLink(htmlHelper, linkText, routeValues, new RouteValueDictionary());
}
public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, IHtmlString linkText, string routeName) {
return RouteLink(htmlHelper, linkText, routeName, (object)null /* routeValues */);
}
public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, IHtmlString linkText, string routeName, object routeValues) {
return RouteLink(htmlHelper, linkText, routeName, ObjectToDictionary(routeValues));
}
public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, IHtmlString linkText, string routeName, RouteValueDictionary routeValues) {
return RouteLink(htmlHelper, linkText, routeName, routeValues, new RouteValueDictionary());
}
public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, IHtmlString linkText, object routeValues, object htmlAttributes) {
return RouteLink(htmlHelper, linkText, ObjectToDictionary(routeValues), HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, IHtmlString linkText, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
return RouteLink(htmlHelper, linkText, null /* routeName */, routeValues, htmlAttributes);
}
public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, IHtmlString linkText, string routeName, object routeValues, object htmlAttributes) {
return RouteLink(htmlHelper, linkText, routeName, ObjectToDictionary(routeValues), HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, IHtmlString linkText, string routeName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
if (String.IsNullOrEmpty(linkText.ToString())) {
throw new ArgumentException("Argument must be a non empty string", "linkText");
}
return MvcHtmlString.Create(GenerateRouteLink(htmlHelper.ViewContext.RequestContext, htmlHelper.RouteCollection, linkText, routeName, routeValues, htmlAttributes));
}
public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, IHtmlString linkText, string routeName, string protocol, string hostName, string fragment, object routeValues, object htmlAttributes) {
return RouteLink(htmlHelper, linkText, routeName, protocol, hostName, fragment, ObjectToDictionary(routeValues), HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, IHtmlString linkText, string routeName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
if (String.IsNullOrEmpty(linkText.ToString())) {
throw new ArgumentException("Argument must be a non empty string", "linkText");
}
return MvcHtmlString.Create(GenerateRouteLink(htmlHelper.ViewContext.RequestContext, htmlHelper.RouteCollection, linkText, routeName, protocol, hostName, fragment, routeValues, htmlAttributes));
}
public static string GenerateLink(RequestContext requestContext, RouteCollection routeCollection, IHtmlString linkText, string routeName, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
return GenerateLink(requestContext, routeCollection, linkText, routeName, actionName, controllerName, null /* protocol */, null /* hostName */, null /* fragment */, routeValues, htmlAttributes);
}
public static string GenerateLink(RequestContext requestContext, RouteCollection routeCollection, IHtmlString linkText, string routeName, string actionName, string controllerName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
return GenerateLinkInternal(requestContext, routeCollection, linkText, routeName, actionName, controllerName, protocol, hostName, fragment, routeValues, htmlAttributes, true /* includeImplicitMvcValues */);
}
public static string GenerateRouteLink(RequestContext requestContext, RouteCollection routeCollection, IHtmlString linkText, string routeName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
return GenerateRouteLink(requestContext, routeCollection, linkText, routeName, null /* protocol */, null /* hostName */, null /* fragment */, routeValues, htmlAttributes);
}
public static string GenerateRouteLink(RequestContext requestContext, RouteCollection routeCollection, IHtmlString linkText, string routeName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
return GenerateLinkInternal(requestContext, routeCollection, linkText, routeName, null /* actionName */, null /* controllerName */, protocol, hostName, fragment, routeValues, htmlAttributes, false /* includeImplicitMvcValues */);
}
private static string GenerateLinkInternal(RequestContext requestContext, RouteCollection routeCollection, IHtmlString linkText, string routeName, string actionName, string controllerName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes, bool includeImplicitMvcValues) {
string url = UrlHelper.GenerateUrl(routeName, actionName, controllerName, protocol, hostName, fragment, routeValues, routeCollection, requestContext, includeImplicitMvcValues);
TagBuilder tagBuilder = new TagBuilder("a") {
InnerHtml = linkText.ToString()
};
tagBuilder.MergeAttributes(htmlAttributes);
tagBuilder.MergeAttribute("href", url);
return tagBuilder.ToString(TagRenderMode.Normal);
}
private static RouteValueDictionary ObjectToDictionary(object value) {
RouteValueDictionary dictionary = new RouteValueDictionary();
if (value != null) {
foreach (var prop in value.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) {
dictionary.Add(prop.Name, prop.GetValue(value));
}
}
return dictionary;
}
}
}

View File

@@ -11,6 +11,7 @@ using System.Web.Routing;
using Autofac;
using Orchard.Mvc.Routes;
using Orchard.Settings;
using Orchard.Exceptions;
namespace Orchard.Mvc {
public class MvcModule : Module {
@@ -31,7 +32,11 @@ namespace Orchard.Mvc {
// The "Request" property throws at application startup on IIS integrated pipeline mode.
var req = HttpContext.Current.Request;
}
catch (Exception) {
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
return false;
}

View File

@@ -188,6 +188,7 @@
<Compile Include="Security\IMembershipValidationService.cs" />
<Compile Include="Localization\Services\ILocalizationStreamParser.cs" />
<Compile Include="Localization\Services\LocalizationStreamParser.cs" />
<Compile Include="Mvc\Html\LinkExtensions.cs" />
<Compile Include="Security\ISslSettingsProvider.cs" />
<Compile Include="Security\NullMembershipService.cs" />
<Compile Include="Security\Providers\DefaultSslSettingsProvider.cs" />
@@ -401,7 +402,7 @@
<Compile Include="Services\IJsonConverter.cs" />
<Compile Include="Settings\CurrentSiteWorkContext.cs" />
<Compile Include="Settings\ResourceDebugMode.cs" />
<Compile Include="Tasks\Locking\Migrations\FrameworkMigrations.cs" />
<Compile Include="Tasks\Locking\Services\DistributedLockSchemaBuilder.cs" />
<Compile Include="Tasks\Locking\Services\IDistributedLock.cs" />
<Compile Include="Tasks\Locking\Services\DistributedLock.cs" />
<Compile Include="Tasks\Locking\Services\IDistributedLockService.cs" />

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using Orchard.Data;
using Orchard.Environment.Configuration;
using Orchard.Logging;
using Orchard.Exceptions;
namespace Orchard.Tasks {
@@ -39,9 +40,13 @@ namespace Orchard.Tasks {
task.Sweep();
Logger.Information("Finished processing background task \"{0}\" on tenant \"{1}\".", taskName, _shellName);
}
catch (Exception e) {
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
_transactionManager.Cancel();
Logger.Error(e, "Error while processing background task \"{0}\" on tenant \"{1}\".", taskName, _shellName);
Logger.Error(ex, "Error while processing background task \"{0}\" on tenant \"{1}\".", taskName, _shellName);
}
}
}

View File

@@ -1,22 +0,0 @@
using System;
using Orchard.Data.Migration;
namespace Orchard.Tasks.Locking.Migrations {
public class FrameworkMigrations : DataMigrationImpl {
public int Create() {
SchemaBuilder.CreateTable("DistributedLockRecord", table => table
.Column<int>("Id", column => column.PrimaryKey().Identity())
.Column<string>("Name", column => column.NotNull().WithLength(512).Unique())
.Column<string>("MachineName", column => column.WithLength(256))
.Column<DateTime>("CreatedUtc")
.Column<DateTime>("ValidUntilUtc", column => column.Nullable()));
SchemaBuilder.AlterTable("DistributedLockRecord", table => {
table.CreateIndex("IDX_DistributedLockRecord_Name", "Name");
});
return 1;
}
}
}

View File

@@ -0,0 +1,48 @@
using System;
using Orchard.Data.Migration.Schema;
using Orchard.Environment.Configuration;
namespace Orchard.Tasks.Locking.Services {
public class DistributedLockSchemaBuilder {
private readonly ShellSettings _shellSettings;
private readonly SchemaBuilder _schemaBuilder;
private const string TableName = "Orchard_Framework_DistributedLockRecord";
public DistributedLockSchemaBuilder(ShellSettings shellSettings, SchemaBuilder schemaBuilder) {
_shellSettings = shellSettings;
_schemaBuilder = schemaBuilder;
}
public bool EnsureSchema() {
if (SchemaExists())
return false;
CreateSchema();
return true;
}
public void CreateSchema() {
_schemaBuilder.CreateTable(TableName, table => table
.Column<int>("Id", column => column.PrimaryKey().Identity())
.Column<string>("Name", column => column.NotNull().WithLength(512).Unique())
.Column<string>("MachineName", column => column.WithLength(256))
.Column<DateTime>("CreatedUtc")
.Column<DateTime>("ValidUntilUtc", column => column.Nullable()));
_schemaBuilder.AlterTable(TableName, table => {
table.CreateIndex("IDX_DistributedLockRecord_Name", "Name");
});
}
public bool SchemaExists() {
try {
var tablePrefix = String.IsNullOrEmpty(_shellSettings.DataTablePrefix) ? "" : _shellSettings.DataTablePrefix + "_";
_schemaBuilder.ExecuteSql(String.Format("select * from {0}{1}", tablePrefix, TableName));
return true;
}
catch {
return false;
}
}
}
}

View File

@@ -44,30 +44,30 @@ namespace Orchard.Tasks.Locking.Services {
if (dLock != null) {
Logger.Debug("Successfully acquired lock '{0}'.", name);
return true;
}
}
Logger.Warning("Failed to acquire lock '{0}' within the specified timeout ({1}).", name, timeout);
}
catch (Exception ex) {
Logger.Error(ex, "Error while trying to acquire lock '{0}'.", name);
// TODO: Is it correct to not throw here? Should we instead ONLY swallow TimeoutException?
}
}
dLock = null;
return false;
}
public IDistributedLock AcquireLock(string name, TimeSpan? maxValidFor, TimeSpan? timeout) {
try {
DistributedLock result = AcquireLockInternal(name, maxValidFor, timeout, throwOnTimeout: true);
Logger.Debug("Successfully acquired lock '{0}'.", name);
return result;
}
}
catch (Exception ex) {
Logger.Error(ex, "Error while trying to acquire lock '{0}'.", name);
throw;
}
}
}
private DistributedLock AcquireLockInternal(string name, TimeSpan? maxValidFor, TimeSpan? timeout, bool throwOnTimeout) {
var internalName = GetInternalLockName(name);
@@ -81,7 +81,7 @@ namespace Orchard.Tasks.Locking.Services {
throw new TimeoutException(String.Format("Failed to acquire lock '{0}' within the specified timeout ({1}).", internalName, timeout));
return null;
}
}
Logger.Debug("Successfully entered local monitor for lock '{0}'.", internalName);
@@ -106,34 +106,34 @@ namespace Orchard.Tasks.Locking.Services {
dLock = new DistributedLock(name, internalName, releaseLockAction: () => {
Monitor.Exit(monitorObj);
DeleteDistributedLockRecord(internalName);
});
});
_locks.Add(monitorObj, dLock);
return true;
}
}
return false;
});
});
if (!success) {
Logger.Debug("Record for lock '{0}' could not be created for current machine within the specified timeout ({1}).", internalName, timeout);
if (throwOnTimeout)
throw new TimeoutException(String.Format("Failed to acquire lock '{0}' within the specified timeout ({1}).", internalName, timeout));
return null;
}
return null;
}
}
return dLock;
}
return dLock;
}
catch (Exception ex) {
Monitor.Exit(monitorObj);
Logger.Error(ex, "An error occurred while trying to acquire lock '{0}'.", internalName);
throw;
}
}
}
}
private bool EnsureDistributedLockRecord(string internalName, TimeSpan? maxValidFor) {
var localMachineName = _machineNameProvider.GetMachineName();
@@ -145,7 +145,7 @@ namespace Orchard.Tasks.Locking.Services {
if (record == null) {
// No record existed, so we're good to create a new one.
Logger.Debug("No valid record was found for lock '{0}'; creating a new record.", internalName);
repository.Create(new DistributedLockRecord {
Name = internalName,
MachineName = localMachineName,
@@ -163,7 +163,7 @@ namespace Orchard.Tasks.Locking.Services {
});
return hasLockRecord;
}
}
private void DeleteDistributedLockRecord(string internalName) {
try {
@@ -177,9 +177,9 @@ namespace Orchard.Tasks.Locking.Services {
}
catch (Exception ex) {
if (ex.IsFatal())
throw;
throw;
Logger.Warning(ex, "An error occurred while deleting record for lock '{0}'.", internalName);
}
}
}
private bool RepeatUntilTimeout(TimeSpan? timeout, TimeSpan repeatInterval, Func<bool> action) {
@@ -198,14 +198,14 @@ namespace Orchard.Tasks.Locking.Services {
if (action == null)
throw new ArgumentNullException();
using (var childLifetimeScope = _lifetimeScope.BeginLifetimeScope()) {
var repository = childLifetimeScope.Resolve<IRepository<DistributedLockRecord>>();
var transactionManager = childLifetimeScope.Resolve<ITransactionManager>();
transactionManager.RequireNew(IsolationLevel.ReadCommitted);
action(repository);
using (var childLifetimeScope = _lifetimeScope.BeginLifetimeScope()) {
var repository = childLifetimeScope.Resolve<IRepository<DistributedLockRecord>>();
var transactionManager = childLifetimeScope.Resolve<ITransactionManager>();
transactionManager.RequireNew(IsolationLevel.ReadCommitted);
action(repository);
}
}
}
private string GetInternalLockName(string name) {
// Prefix the requested lock name by a constant and the tenant name.
return String.Format("DistributedLock:{0}:{1}", _shellSettings.Name, name);

View File

@@ -1,6 +1,7 @@
using System;
using System.Timers;
using Orchard.Logging;
using Orchard.Exceptions;
namespace Orchard.Tasks {
@@ -51,6 +52,10 @@ namespace Orchard.Tasks {
}
}
catch (Exception ex) {
if (ex.IsFatal()) {
throw;
}
Logger.Warning(ex, "Problem in background tasks");
}
finally {

View File

@@ -1,6 +1,7 @@
using System;
using System.Web;
using Orchard.Logging;
using Orchard.Exceptions;
namespace Orchard.Time {
/// <summary>
@@ -31,8 +32,11 @@ namespace Orchard.Time {
TimeZone = TimeZoneInfo.FindSystemTimeZoneById(siteTimeZoneId)
};
}
catch(Exception e) {
Logger.Error(e, "TimeZone could not be loaded");
catch(Exception ex) {
if (ex.IsFatal()) {
throw;
}
Logger.Error(ex, "TimeZone could not be loaded");
// if the database could not be updated in time, ignore this provider
return null;

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using Orchard.Logging;
using Orchard.UI.Notify;
using Orchard.Exceptions;
namespace Orchard.UI.Admin.Notification {
public class NotificationManager : INotificationManager {
@@ -22,8 +23,11 @@ namespace Orchard.UI.Admin.Notification {
try {
return n.GetNotifications();
}
catch(Exception e) {
Logger.Error("An unhandled exception was thrown while generating a notification: " + n.GetType(), e);
catch(Exception ex) {
if (ex.IsFatal()) {
throw;
}
Logger.Error("An unhandled exception was thrown while generating a notification: " + n.GetType(), ex);
return Enumerable.Empty<NotifyEntry>();
}
}).ToList();

View File

@@ -16,8 +16,7 @@ namespace Orchard.Utility.Extensions {
var sb = new StringBuilder(camel);
for (int i = camel.Length-1; i>0; i--) {
var current = sb[i];
if('A' <= current && current <= 'Z') {
if(char.IsUpper(sb[i])) {
sb.Insert(i, ' ');
}
}
@@ -357,4 +356,4 @@ namespace Orchard.Utility.Extensions {
return Encoding.UTF8.GetString(Convert.FromBase64String(value));
}
}
}
}

View File

@@ -16,7 +16,8 @@
"gulp-uglify": "^1.2.0",
"gulp-rename": "^1.2.2",
"gulp-concat": "^2.5.2",
"gulp-header": "^1.2.2"
"gulp-header": "^1.2.2",
"fs": "^0.0.2"
},
"dependencies": { }
}