mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2026-02-09 09:16:41 +08:00
Merge branch '1.9.x' into dev
Conflicts: src/Orchard.Web/Modules/Orchard.DynamicForms/Module.txt src/Orchard.Web/Modules/Orchard.Rules/Module.txt
This commit is contained in:
@@ -6,33 +6,33 @@ Version: 1.9.1
|
|||||||
OrchardVersion: 1.9
|
OrchardVersion: 1.9
|
||||||
Description: Create custom forms like contact forms using layouts.
|
Description: Create custom forms like contact forms using layouts.
|
||||||
Features:
|
Features:
|
||||||
Orchard.DynamicForms:
|
Orchard.DynamicForms:
|
||||||
Name: Dynamic Forms
|
Name: Dynamic Forms
|
||||||
Description: Create custom forms like contact forms using layouts.
|
Description: Create custom forms like contact forms using layouts.
|
||||||
Category: Forms
|
Category: Forms
|
||||||
Dependencies: Orchard.Layouts, Orchard.Tokens, Orchard.Workflows, Orchard.Fields, Common
|
Dependencies: Orchard.Layouts, Orchard.Tokens, Orchard.Workflows, Orchard.Fields, Common
|
||||||
Orchard.DynamicForms.AntiSpam:
|
Orchard.DynamicForms.AntiSpam:
|
||||||
Name: Anti-Spam Elements
|
Name: Anti-Spam Elements
|
||||||
Description: Provides anti-spam elements to protect your content from malicious submissions.
|
Description: Provides anti-spam elements to protect your content from malicious submissions.
|
||||||
Category: Forms
|
Category: Forms
|
||||||
Dependencies: Orchard.DynamicForms, Orchard.AntiSpam
|
Dependencies: Orchard.DynamicForms, Orchard.AntiSpam
|
||||||
Orchard.DynamicForms.Taxonomies:
|
Orchard.DynamicForms.Taxonomies:
|
||||||
Name: Taxonomy Element
|
Name: Taxonomy Element
|
||||||
Description: Adds a Taxonomy form element to the sytem.
|
Description: Adds a Taxonomy form element to the system.
|
||||||
Category: Forms
|
Category: Forms
|
||||||
Dependencies: Orchard.DynamicForms, Orchard.Taxonomies
|
Dependencies: Orchard.DynamicForms, Orchard.Taxonomies
|
||||||
Orchard.DynamicForms.Projections:
|
Orchard.DynamicForms.Projections:
|
||||||
Name: Query Element
|
Name: Query Element
|
||||||
Description: Adds a Query form element to the sytem.
|
Description: Adds a Query form element to the system.
|
||||||
Category: Forms
|
Category: Forms
|
||||||
Dependencies: Orchard.DynamicForms, Orchard.Projections
|
Dependencies: Orchard.DynamicForms, Orchard.Projections
|
||||||
Orchard.DynamicForms.Activities.Validation:
|
Orchard.DynamicForms.Activities.Validation:
|
||||||
Name: Dynamic Forms Validation Activities
|
Name: Dynamic Forms Validation Activities
|
||||||
Description: Adds activities for form validation.
|
Description: Adds activities for form validation.
|
||||||
Category: Forms
|
Category: Forms
|
||||||
Dependencies: Orchard.DynamicForms, Orchard.Scripting.CSharp
|
Dependencies: Orchard.DynamicForms, Orchard.Scripting.CSharp
|
||||||
Orchard.DynamicForms.Bindings.Users
|
Orchard.DynamicForms.Bindings.Users
|
||||||
Name: Dynamic Forms User Bindings
|
Name: Dynamic Forms User Bindings
|
||||||
Description: Adds bindings for Users.
|
Description: Adds bindings for Users.
|
||||||
Category: Forms
|
Category: Forms
|
||||||
Dependencies: Orchard.DynamicForms, Orchard.Users
|
Dependencies: Orchard.DynamicForms, Orchard.Users
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ namespace Orchard.OutputCache.Filters {
|
|||||||
|
|
||||||
// Is there a cached item, and are we allowed to serve it?
|
// Is there a cached item, and are we allowed to serve it?
|
||||||
var allowServeFromCache = filterContext.RequestContext.HttpContext.Request.Headers["Cache-Control"] != "no-cache" || CacheSettings.IgnoreNoCache;
|
var allowServeFromCache = filterContext.RequestContext.HttpContext.Request.Headers["Cache-Control"] != "no-cache" || CacheSettings.IgnoreNoCache;
|
||||||
var cacheItem = _cacheStorageProvider.GetCacheItem(_cacheKey);
|
var cacheItem = GetCacheItem(_cacheKey);
|
||||||
if (allowServeFromCache && cacheItem != null) {
|
if (allowServeFromCache && cacheItem != null) {
|
||||||
|
|
||||||
Logger.Debug("Item '{0}' was found in cache.", _cacheKey);
|
Logger.Debug("Item '{0}' was found in cache.", _cacheKey);
|
||||||
@@ -142,7 +142,7 @@ namespace Orchard.OutputCache.Filters {
|
|||||||
|
|
||||||
// Item might now have been rendered and cached by another request; if so serve it from cache.
|
// Item might now have been rendered and cached by another request; if so serve it from cache.
|
||||||
if (allowServeFromCache) {
|
if (allowServeFromCache) {
|
||||||
cacheItem = _cacheStorageProvider.GetCacheItem(_cacheKey);
|
cacheItem = GetCacheItem(_cacheKey);
|
||||||
if (cacheItem != null) {
|
if (cacheItem != null) {
|
||||||
Logger.Debug("Item '{0}' was now found; releasing cache key lock and serving from cache.", _cacheKey);
|
Logger.Debug("Item '{0}' was now found; releasing cache key lock and serving from cache.", _cacheKey);
|
||||||
Monitor.Exit(cacheKeyLock);
|
Monitor.Exit(cacheKeyLock);
|
||||||
@@ -593,6 +593,18 @@ namespace Orchard.OutputCache.Filters {
|
|||||||
|
|
||||||
return keyBuilder.ToString();
|
return keyBuilder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual CacheItem GetCacheItem(string key) {
|
||||||
|
try {
|
||||||
|
var cacheItem = _cacheStorageProvider.GetCacheItem(key);
|
||||||
|
return cacheItem;
|
||||||
|
}
|
||||||
|
catch(Exception e) {
|
||||||
|
Logger.Error(e, "An unexpected error occured while reading a cache entry");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ViewDataContainer : IViewDataContainer {
|
public class ViewDataContainer : IViewDataContainer {
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
namespace Orchard.OutputCache.Models {
|
namespace Orchard.OutputCache.Models {
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class CacheItem {
|
public class CacheItem {
|
||||||
|
// used for serialization compatibility
|
||||||
|
public static readonly string Version = "1";
|
||||||
|
|
||||||
public DateTime CachedOnUtc { get; set; }
|
public DateTime CachedOnUtc { get; set; }
|
||||||
public int Duration { get; set; }
|
public int Duration { get; set; }
|
||||||
public int GraceTime { get; set; }
|
public int GraceTime { get; set; }
|
||||||
|
|||||||
@@ -7,9 +7,16 @@ OrchardVersion: 1.9
|
|||||||
Description: Adds output caching functionality
|
Description: Adds output caching functionality
|
||||||
Features:
|
Features:
|
||||||
Orchard.OutputCache:
|
Orchard.OutputCache:
|
||||||
|
Name: Output Cache
|
||||||
Description: Adds output caching functionality.
|
Description: Adds output caching functionality.
|
||||||
Category: Performance
|
Category: Performance
|
||||||
Orchard.OutputCache.Database:
|
Orchard.OutputCache.Database:
|
||||||
|
Name: Database Output Cache
|
||||||
Description: Activates a provider that stores output cache data in the database.
|
Description: Activates a provider that stores output cache data in the database.
|
||||||
Category: Performance
|
Category: Performance
|
||||||
Dependencies: Orchard.OutputCache
|
Dependencies: Orchard.OutputCache
|
||||||
|
Orchard.OutputCache.FileSystem:
|
||||||
|
Name: File System Output Cache
|
||||||
|
Description: Activates a provider that stores output cache data in the App_Data folder.
|
||||||
|
Category: Performance
|
||||||
|
Dependencies: Orchard.OutputCache
|
||||||
|
|||||||
@@ -110,7 +110,9 @@
|
|||||||
<Compile Include="Models\CacheSettingsPart.cs" />
|
<Compile Include="Models\CacheSettingsPart.cs" />
|
||||||
<Compile Include="Models\CacheParameterRecord.cs" />
|
<Compile Include="Models\CacheParameterRecord.cs" />
|
||||||
<Compile Include="Services\CacheService.cs" />
|
<Compile Include="Services\CacheService.cs" />
|
||||||
|
<Compile Include="Services\FileSystemOutputCacheBackgroundTask.cs" />
|
||||||
<Compile Include="Services\DatabaseOutputCacheBackgroundTask.cs" />
|
<Compile Include="Services\DatabaseOutputCacheBackgroundTask.cs" />
|
||||||
|
<Compile Include="Services\FileSystemOutputCacheProvider.cs" />
|
||||||
<Compile Include="Services\DatabaseOutputCacheProvider.cs" />
|
<Compile Include="Services\DatabaseOutputCacheProvider.cs" />
|
||||||
<Compile Include="Services\DefaultCacheControlStrategy.cs" />
|
<Compile Include="Services\DefaultCacheControlStrategy.cs" />
|
||||||
<Compile Include="Services\DefaultTagCache.cs" />
|
<Compile Include="Services\DefaultTagCache.cs" />
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Orchard.Caching;
|
||||||
|
using Orchard.Environment.Configuration;
|
||||||
|
using Orchard.Environment.Extensions;
|
||||||
|
using Orchard.FileSystems.AppData;
|
||||||
|
using Orchard.Services;
|
||||||
|
using Orchard.Tasks;
|
||||||
|
|
||||||
|
namespace Orchard.OutputCache.Services {
|
||||||
|
[OrchardFeature("Orchard.OutputCache.FileSystem")]
|
||||||
|
/// <summary>
|
||||||
|
/// A background task deleting all App_Data output cache content.
|
||||||
|
/// </summary>
|
||||||
|
public class FileSystemOutputCacheBackgroundTask : IBackgroundTask {
|
||||||
|
private readonly IAppDataFolder _appDataFolder;
|
||||||
|
private readonly ShellSettings _shellSettings;
|
||||||
|
private readonly ICacheManager _cacheManager;
|
||||||
|
private readonly IClock _clock;
|
||||||
|
private readonly ISignals _signals;
|
||||||
|
|
||||||
|
private string _root;
|
||||||
|
|
||||||
|
public FileSystemOutputCacheBackgroundTask(
|
||||||
|
IAppDataFolder appDataFolder,
|
||||||
|
ShellSettings shellSettings,
|
||||||
|
ICacheManager cacheManager,
|
||||||
|
IClock clock,
|
||||||
|
ISignals signals) {
|
||||||
|
_appDataFolder = appDataFolder;
|
||||||
|
_shellSettings = shellSettings;
|
||||||
|
_cacheManager = cacheManager;
|
||||||
|
_clock = clock;
|
||||||
|
_signals = signals;
|
||||||
|
|
||||||
|
_root = _appDataFolder.Combine("OutputCache", _shellSettings.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Sweep() {
|
||||||
|
foreach(var filename in _appDataFolder.ListFiles(_root).ToArray()) {
|
||||||
|
var validUntilUtc = _cacheManager.Get(filename, context => {
|
||||||
|
_signals.When(filename);
|
||||||
|
|
||||||
|
using (var stream = _appDataFolder.OpenFile(filename)) {
|
||||||
|
var cacheItem = FileSystemOutputCacheStorageProvider.Deserialize(stream);
|
||||||
|
return cacheItem.ValidUntilUtc;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (_clock.UtcNow > validUntilUtc) {
|
||||||
|
_appDataFolder.DeleteFile(filename);
|
||||||
|
_signals.Trigger(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Orchard.OutputCache.Models;
|
||||||
|
using Orchard.Environment.Extensions;
|
||||||
|
using Orchard.Logging;
|
||||||
|
using Orchard.Services;
|
||||||
|
using Orchard.FileSystems.AppData;
|
||||||
|
using Orchard.Environment.Configuration;
|
||||||
|
using System.Web;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.Serialization.Formatters.Binary;
|
||||||
|
|
||||||
|
namespace Orchard.OutputCache.Services {
|
||||||
|
[OrchardFeature("Orchard.OutputCache.FileSystem")]
|
||||||
|
[OrchardSuppressDependency("Orchard.OutputCache.Services.DefaultCacheStorageProvider")]
|
||||||
|
/// <summary>
|
||||||
|
/// This class provides an implementation of <see cref="IOutputCacheStorageProvider"/>
|
||||||
|
/// based on the local App_Data folder, inside <c>OuputCache/{tenant}</c>. It is not
|
||||||
|
/// recommended when used in a server farm.
|
||||||
|
/// The <see cref="CacheItem"/> instances are binary serialized.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This provider doesn't implement quotas support yet.
|
||||||
|
/// </remarks>
|
||||||
|
public class FileSystemOutputCacheStorageProvider : IOutputCacheStorageProvider {
|
||||||
|
private readonly IClock _clock;
|
||||||
|
private readonly IAppDataFolder _appDataFolder;
|
||||||
|
private readonly ShellSettings _shellSettings;
|
||||||
|
private readonly string _root;
|
||||||
|
|
||||||
|
public FileSystemOutputCacheStorageProvider(IClock clock, IAppDataFolder appDataFolder, ShellSettings shellSettings) {
|
||||||
|
_appDataFolder = appDataFolder;
|
||||||
|
_clock = clock;
|
||||||
|
_shellSettings = shellSettings;
|
||||||
|
_root = _appDataFolder.Combine("OutputCache", _shellSettings.Name);
|
||||||
|
|
||||||
|
Logger = NullLogger.Instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILogger Logger { get; set; }
|
||||||
|
|
||||||
|
public void Set(string key, CacheItem cacheItem) {
|
||||||
|
Retry(() => {
|
||||||
|
if (cacheItem == null) {
|
||||||
|
throw new ArgumentNullException("cacheItem");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cacheItem.ValidFor <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var filename = GetCacheItemFilename(key);
|
||||||
|
|
||||||
|
using (var stream = Serialize(cacheItem)) {
|
||||||
|
using (var fileStream = _appDataFolder.CreateFile(filename)) {
|
||||||
|
stream.CopyTo(fileStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(string key) {
|
||||||
|
Retry(() => {
|
||||||
|
var filename = GetCacheItemFilename(key);
|
||||||
|
if (_appDataFolder.FileExists(filename)) {
|
||||||
|
_appDataFolder.DeleteFile(filename);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveAll() {
|
||||||
|
foreach(var filename in _appDataFolder.ListFiles(_root)) {
|
||||||
|
if(_appDataFolder.FileExists(filename)) {
|
||||||
|
_appDataFolder.DeleteFile(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CacheItem GetCacheItem(string key) {
|
||||||
|
return Retry(() => {
|
||||||
|
var filename = GetCacheItemFilename(key);
|
||||||
|
|
||||||
|
if (!_appDataFolder.FileExists(filename)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var stream = _appDataFolder.OpenFile(filename)) {
|
||||||
|
|
||||||
|
if (stream == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Deserialize(stream);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<CacheItem> GetCacheItems(int skip, int count) {
|
||||||
|
return _appDataFolder.ListFiles(_root)
|
||||||
|
.OrderBy(x => x)
|
||||||
|
.Skip(skip)
|
||||||
|
.Take(count)
|
||||||
|
.Select(filename => {
|
||||||
|
using (var stream = _appDataFolder.OpenFile(filename)) {
|
||||||
|
return Deserialize(stream);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetCacheItemsCount() {
|
||||||
|
return _appDataFolder.ListFiles(_root).Count();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetCacheItemFilename(string key) {
|
||||||
|
return _appDataFolder.Combine(_root, HttpUtility.UrlEncode(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static MemoryStream Serialize(CacheItem item) {
|
||||||
|
BinaryFormatter binaryFormatter = new BinaryFormatter();
|
||||||
|
var memoryStream = new MemoryStream();
|
||||||
|
binaryFormatter.Serialize(memoryStream, item);
|
||||||
|
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
return memoryStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static CacheItem Deserialize(Stream stream) {
|
||||||
|
BinaryFormatter binaryFormatter = new BinaryFormatter();
|
||||||
|
var result = (CacheItem)binaryFormatter.Deserialize(stream);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private T Retry<T>(Func<T> action) {
|
||||||
|
var retries = 3;
|
||||||
|
for (int i = 1; i <= retries; i++) {
|
||||||
|
try {
|
||||||
|
var t = action();
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
if (i == retries) {
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return default(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Retry(Action action) {
|
||||||
|
var retries = 3;
|
||||||
|
for(int i=1; i <= retries; i++) {
|
||||||
|
try {
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
if(i == retries) {
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,10 +18,11 @@ namespace Orchard.Redis.Caching {
|
|||||||
private readonly ShellSettings _shellSettings;
|
private readonly ShellSettings _shellSettings;
|
||||||
private readonly IRedisConnectionProvider _redisConnectionProvider;
|
private readonly IRedisConnectionProvider _redisConnectionProvider;
|
||||||
private readonly string _connectionString;
|
private readonly string _connectionString;
|
||||||
|
private readonly ConnectionMultiplexer _connectionMultiplexer;
|
||||||
|
|
||||||
public IDatabase Database {
|
public IDatabase Database {
|
||||||
get {
|
get {
|
||||||
return _redisConnectionProvider.GetConnection(_connectionString).GetDatabase();
|
return _connectionMultiplexer.GetDatabase();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,11 +30,16 @@ namespace Orchard.Redis.Caching {
|
|||||||
_shellSettings = shellSettings;
|
_shellSettings = shellSettings;
|
||||||
_redisConnectionProvider = redisConnectionProvider;
|
_redisConnectionProvider = redisConnectionProvider;
|
||||||
_connectionString = _redisConnectionProvider.GetConnectionString(ConnectionStringKey);
|
_connectionString = _redisConnectionProvider.GetConnectionString(ConnectionStringKey);
|
||||||
|
_connectionMultiplexer = _redisConnectionProvider.GetConnection(_connectionString);
|
||||||
|
|
||||||
Logger = NullLogger.Instance;
|
Logger = NullLogger.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Get<T>(string key) {
|
public object Get<T>(string key) {
|
||||||
var json = Database.StringGet(GetLocalizedKey(key));
|
var json = Database.StringGet(GetLocalizedKey(key));
|
||||||
|
if(String.IsNullOrEmpty(json)) {
|
||||||
|
return default(T);
|
||||||
|
}
|
||||||
return JsonConvert.DeserializeObject<T>(json);
|
return JsonConvert.DeserializeObject<T>(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Orchard.Environment.Configuration;
|
using Orchard.Environment.Configuration;
|
||||||
using Orchard.Environment.Extensions;
|
using Orchard.Environment.Extensions;
|
||||||
using Orchard.Logging;
|
using Orchard.Logging;
|
||||||
@@ -10,11 +9,23 @@ using Orchard.OutputCache.Models;
|
|||||||
using Orchard.OutputCache.Services;
|
using Orchard.OutputCache.Services;
|
||||||
using Orchard.Redis.Extensions;
|
using Orchard.Redis.Extensions;
|
||||||
using StackExchange.Redis;
|
using StackExchange.Redis;
|
||||||
|
using System.Runtime.Serialization.Formatters.Binary;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
|
||||||
namespace Orchard.Redis.OutputCache {
|
namespace Orchard.Redis.OutputCache {
|
||||||
|
|
||||||
[OrchardFeature("Orchard.Redis.OutputCache")]
|
[OrchardFeature("Orchard.Redis.OutputCache")]
|
||||||
[OrchardSuppressDependency("Orchard.OutputCache.Services.DefaultCacheStorageProvider")]
|
[OrchardSuppressDependency("Orchard.OutputCache.Services.DefaultCacheStorageProvider")]
|
||||||
|
/// <summary>
|
||||||
|
/// This implementation stores a <see cref="CacheItem"/> instance to a Redis server.
|
||||||
|
/// The item is serialized using a <see cref="BinaryFormatter"/> and GZipped. We rely
|
||||||
|
/// on compression at this level of the implementation as other output cache providers
|
||||||
|
/// might not want to rely on it, or transform the data to binary. The content is compressed
|
||||||
|
/// as HTML pages can be consequent, like several hundreds of KB, and the network be clogged.
|
||||||
|
/// To prevent versioning issues with serialized data, the Redis keys contain the
|
||||||
|
/// <see cref="CacheItem.Version"/> property.
|
||||||
|
/// </summary>
|
||||||
public class RedisOutputCacheStorageProvider : IOutputCacheStorageProvider {
|
public class RedisOutputCacheStorageProvider : IOutputCacheStorageProvider {
|
||||||
|
|
||||||
private readonly ShellSettings _shellSettings;
|
private readonly ShellSettings _shellSettings;
|
||||||
@@ -43,12 +54,19 @@ namespace Orchard.Redis.OutputCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Set(string key, CacheItem cacheItem) {
|
public void Set(string key, CacheItem cacheItem) {
|
||||||
|
if(cacheItem == null) {
|
||||||
|
throw new ArgumentNullException("cacheItem");
|
||||||
|
}
|
||||||
|
|
||||||
if (cacheItem.ValidFor <= 0) {
|
if (cacheItem.ValidFor <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var value = JsonConvert.SerializeObject(cacheItem);
|
using (var decompressedStream = Serialize(cacheItem)) {
|
||||||
Database.StringSet(GetLocalizedKey(key), value, TimeSpan.FromSeconds(cacheItem.ValidFor));
|
using (var compressedStream = Compress(decompressedStream)) {
|
||||||
|
Database.StringSet(GetLocalizedKey(key), compressedStream.ToArray(), TimeSpan.FromSeconds(cacheItem.ValidFor));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Remove(string key) {
|
public void Remove(string key) {
|
||||||
@@ -60,12 +78,21 @@ namespace Orchard.Redis.OutputCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public CacheItem GetCacheItem(string key) {
|
public CacheItem GetCacheItem(string key) {
|
||||||
string value = Database.StringGet(GetLocalizedKey(key));
|
var value = Database.StringGet(GetLocalizedKey(key));
|
||||||
if (String.IsNullOrEmpty(value)) {
|
|
||||||
|
if (value.IsNullOrEmpty) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return JsonConvert.DeserializeObject<CacheItem>(value);
|
using (var compressedStream = new MemoryStream(value)) {
|
||||||
|
if(compressedStream.Length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
using(var decompressedStream = Decompress(compressedStream)) {
|
||||||
|
return Deserialize(decompressedStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<CacheItem> GetCacheItems(int skip, int count) {
|
public IEnumerable<CacheItem> GetCacheItems(int skip, int count) {
|
||||||
@@ -88,7 +115,7 @@ namespace Orchard.Redis.OutputCache {
|
|||||||
/// <param name="key">The key to localized.</param>
|
/// <param name="key">The key to localized.</param>
|
||||||
/// <returns>A localized key based on the tenant name.</returns>
|
/// <returns>A localized key based on the tenant name.</returns>
|
||||||
private string GetLocalizedKey(string key) {
|
private string GetLocalizedKey(string key) {
|
||||||
return _shellSettings.Name + ":OutputCache:" + key;
|
return _shellSettings.Name + ":OC:" + CacheItem.Version + ":" + key;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -111,5 +138,37 @@ namespace Orchard.Redis.OutputCache {
|
|||||||
|
|
||||||
return _keysCache;
|
return _keysCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static MemoryStream Serialize(CacheItem item) {
|
||||||
|
BinaryFormatter binaryFormatter = new BinaryFormatter();
|
||||||
|
var memoryStream = new MemoryStream();
|
||||||
|
binaryFormatter.Serialize(memoryStream, item);
|
||||||
|
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
return memoryStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CacheItem Deserialize(Stream stream) {
|
||||||
|
BinaryFormatter binaryFormatter = new BinaryFormatter();
|
||||||
|
var result = (CacheItem)binaryFormatter.Deserialize(stream);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MemoryStream Compress(Stream stream) {
|
||||||
|
var compressedStream = new MemoryStream();
|
||||||
|
using (var compressionStream = new GZipStream(compressedStream, CompressionMode.Compress)) {
|
||||||
|
stream.CopyTo(compressionStream);
|
||||||
|
return compressedStream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Stream Decompress(Stream stream) {
|
||||||
|
var decompressedStream = new MemoryStream();
|
||||||
|
using (GZipStream decompressionStream = new GZipStream(stream, CompressionMode.Decompress)) {
|
||||||
|
decompressionStream.CopyTo(decompressedStream);
|
||||||
|
decompressedStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
return decompressedStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,43 +1,49 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Web;
|
using Orchard.Environment.Configuration;
|
||||||
using NHibernate.Util;
|
|
||||||
using Orchard.Environment.Extensions;
|
using Orchard.Environment.Extensions;
|
||||||
using Orchard.OutputCache.Services;
|
using Orchard.OutputCache.Services;
|
||||||
using Orchard.Redis.Configuration;
|
using Orchard.Redis.Configuration;
|
||||||
using StackExchange.Redis;
|
using StackExchange.Redis;
|
||||||
|
|
||||||
namespace Orchard.Redis.OutputCache
|
namespace Orchard.Redis.OutputCache {
|
||||||
{
|
|
||||||
[OrchardFeature("Orchard.Redis.OutputCache")]
|
[OrchardFeature("Orchard.Redis.OutputCache")]
|
||||||
[OrchardSuppressDependency("Orchard.OutputCache.Services.DefaultTagCache")]
|
[OrchardSuppressDependency("Orchard.OutputCache.Services.DefaultTagCache")]
|
||||||
public class RedisTagCache : ITagCache {
|
public class RedisTagCache : ITagCache {
|
||||||
private readonly IRedisConnectionProvider _redisConnectionProvider;
|
private readonly IRedisConnectionProvider _redisConnectionProvider;
|
||||||
private readonly string _connectionString;
|
private readonly string _connectionString;
|
||||||
|
private readonly ConnectionMultiplexer _connectionMultiplexer;
|
||||||
|
private readonly ShellSettings _shellSettings;
|
||||||
|
|
||||||
public RedisTagCache(IRedisConnectionProvider redisConnectionProvider) {
|
public RedisTagCache(IRedisConnectionProvider redisConnectionProvider, ShellSettings shellSettings) {
|
||||||
_redisConnectionProvider = redisConnectionProvider;
|
_redisConnectionProvider = redisConnectionProvider;
|
||||||
_connectionString = _redisConnectionProvider.GetConnectionString(RedisOutputCacheStorageProvider.ConnectionStringKey);
|
_connectionString = _redisConnectionProvider.GetConnectionString(RedisOutputCacheStorageProvider.ConnectionStringKey);
|
||||||
|
_connectionMultiplexer = _redisConnectionProvider.GetConnection(_connectionString);
|
||||||
|
_shellSettings = shellSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IDatabase Database {
|
private IDatabase Database {
|
||||||
get { return _redisConnectionProvider.GetConnection(_connectionString).GetDatabase(); }
|
get { return _connectionMultiplexer.GetDatabase(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Tag(string tag, params string[] keys) {
|
public void Tag(string tag, params string[] keys) {
|
||||||
Database.SetAdd(tag, Array.ConvertAll(keys, x=> (RedisValue) x));
|
Database.SetAdd(GetLocalizedKey(tag), Array.ConvertAll(keys, x=> (RedisValue) x));
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<string> GetTaggedItems(string tag) {
|
public IEnumerable<string> GetTaggedItems(string tag) {
|
||||||
var values = Database.SetMembers(tag);
|
var values = Database.SetMembers(GetLocalizedKey(tag));
|
||||||
if (values == null || values.Length == 0)
|
if (values == null || values.Length == 0)
|
||||||
return Enumerable.Empty<string>();
|
return Enumerable.Empty<string>();
|
||||||
return Array.ConvertAll(values, x => (string) x);
|
return Array.ConvertAll(values, x => (string) x);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveTag(string tag) {
|
public void RemoveTag(string tag) {
|
||||||
Database.KeyDelete(tag);
|
Database.KeyDelete(GetLocalizedKey(tag));
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetLocalizedKey(string key) {
|
||||||
|
return _shellSettings.Name + ":Tag:" + key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,10 +5,10 @@ Website: http://orchardrules.codeplex.com
|
|||||||
Version: 1.8
|
Version: 1.8
|
||||||
OrchardVersion: 1.9
|
OrchardVersion: 1.9
|
||||||
LifecycleStatus: Deprecated
|
LifecycleStatus: Deprecated
|
||||||
Description: Provides a system de trigger actions based on events.
|
Description: Provides a system to trigger actions based on events.
|
||||||
Features:
|
Features:
|
||||||
Orchard.Rules:
|
Orchard.Rules:
|
||||||
Name: Rules
|
Name: Rules
|
||||||
Description: Provides a system de trigger actions based on events.
|
Description: Provides a system to trigger actions based on events.
|
||||||
Dependencies: Orchard.Tokens, Orchard.Scripting, Orchard.Forms
|
Dependencies: Orchard.Tokens, Orchard.Scripting, Orchard.Forms
|
||||||
Category: Rules
|
Category: Rules
|
||||||
@@ -61,7 +61,7 @@ namespace Orchard.Widgets.Controllers {
|
|||||||
return RedirectToAction("Index", "Admin", new { area = "Dashboard" });
|
return RedirectToAction("Index", "Admin", new { area = "Dashboard" });
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerable<LayerPart> layers = _widgetsService.GetLayers().ToList();
|
IEnumerable<LayerPart> layers = _widgetsService.GetLayers().OrderBy(x => x.Name).ToList();
|
||||||
|
|
||||||
if (!layers.Any()) {
|
if (!layers.Any()) {
|
||||||
Services.Notifier.Error(T("There are no widget layers defined. A layer will need to be added in order to add widgets to any part of the site."));
|
Services.Notifier.Error(T("There are no widget layers defined. A layer will need to be added in order to add widgets to any part of the site."));
|
||||||
@@ -87,7 +87,7 @@ namespace Orchard.Widgets.Controllers {
|
|||||||
|
|
||||||
if (!String.IsNullOrWhiteSpace(culture)) {
|
if (!String.IsNullOrWhiteSpace(culture)) {
|
||||||
widgets = widgets.Where(x => {
|
widgets = widgets.Where(x => {
|
||||||
if(x.Has<ILocalizableAspect>()) {
|
if (x.Has<ILocalizableAspect>()) {
|
||||||
return String.Equals(x.As<ILocalizableAspect>().Culture, culture, StringComparison.InvariantCultureIgnoreCase);
|
return String.Equals(x.As<ILocalizableAspect>().Culture, culture, StringComparison.InvariantCultureIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ namespace Orchard.Widgets.Controllers {
|
|||||||
return RedirectToAction("Index");
|
return RedirectToAction("Index");
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerable<LayerPart> layers = _widgetsService.GetLayers().ToList();
|
IEnumerable<LayerPart> layers = _widgetsService.GetLayers().OrderBy(x => x.Name).ToList();
|
||||||
|
|
||||||
if (!layers.Any()) {
|
if (!layers.Any()) {
|
||||||
Services.Notifier.Error(T("Layer not found: {0}", layerId));
|
Services.Notifier.Error(T("Layer not found: {0}", layerId));
|
||||||
@@ -315,7 +315,8 @@ namespace Orchard.Widgets.Controllers {
|
|||||||
try {
|
try {
|
||||||
_widgetsService.DeleteLayer(id);
|
_widgetsService.DeleteLayer(id);
|
||||||
Services.Notifier.Information(T("Layer was successfully deleted"));
|
Services.Notifier.Information(T("Layer was successfully deleted"));
|
||||||
} catch (Exception exception) {
|
}
|
||||||
|
catch (Exception exception) {
|
||||||
Logger.Error(T("Removing Layer failed: {0}", exception.Message).Text);
|
Logger.Error(T("Removing Layer failed: {0}", exception.Message).Text);
|
||||||
Services.Notifier.Error(T("Removing Layer failed: {0}", exception.Message));
|
Services.Notifier.Error(T("Removing Layer failed: {0}", exception.Message));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -219,8 +219,8 @@ namespace Upgrade.Controllers {
|
|||||||
site.As<InfosetPart>().Store("RegistrationSettingsPart", "UsersCanRegister", (bool)reader["UsersCanRegister"]);
|
site.As<InfosetPart>().Store("RegistrationSettingsPart", "UsersCanRegister", (bool)reader["UsersCanRegister"]);
|
||||||
site.As<InfosetPart>().Store("RegistrationSettingsPart", "UsersMustValidateEmail", (bool)reader["UsersMustValidateEmail"]);
|
site.As<InfosetPart>().Store("RegistrationSettingsPart", "UsersMustValidateEmail", (bool)reader["UsersMustValidateEmail"]);
|
||||||
site.As<InfosetPart>().Store("RegistrationSettingsPart", "UsersCanRegister", (bool)reader["UsersCanRegister"]);
|
site.As<InfosetPart>().Store("RegistrationSettingsPart", "UsersCanRegister", (bool)reader["UsersCanRegister"]);
|
||||||
site.As<InfosetPart>().Store("RegistrationSettingsPart", "ValidateEmailRegisteredWebsite", ConvertToBool(reader["ValidateEmailRegisteredWebsite"]));
|
site.As<InfosetPart>().Store("RegistrationSettingsPart", "ValidateEmailRegisteredWebsite", ConvertToString(reader["ValidateEmailRegisteredWebsite"]));
|
||||||
site.As<InfosetPart>().Store("RegistrationSettingsPart", "ValidateEmailContactEMail", ConvertToBool(reader["ValidateEmailContactEMail"]));
|
site.As<InfosetPart>().Store("RegistrationSettingsPart", "ValidateEmailContactEmail", ConvertToString(reader["ValidateEmailContactEmail"]));
|
||||||
site.As<InfosetPart>().Store("RegistrationSettingsPart", "UsersAreModerated", (bool)reader["UsersAreModerated"]);
|
site.As<InfosetPart>().Store("RegistrationSettingsPart", "UsersAreModerated", (bool)reader["UsersAreModerated"]);
|
||||||
site.As<InfosetPart>().Store("RegistrationSettingsPart", "NotifyModeration", (bool)reader["NotifyModeration"]);
|
site.As<InfosetPart>().Store("RegistrationSettingsPart", "NotifyModeration", (bool)reader["NotifyModeration"]);
|
||||||
site.As<InfosetPart>().Store("RegistrationSettingsPart", "NotificationsRecipients", ConvertToBool(reader["NotificationsRecipients"]));
|
site.As<InfosetPart>().Store("RegistrationSettingsPart", "NotificationsRecipients", ConvertToBool(reader["NotificationsRecipients"]));
|
||||||
|
|||||||
@@ -119,6 +119,14 @@
|
|||||||
<requestLimits maxAllowedContentLength="67108864"/>
|
<requestLimits maxAllowedContentLength="67108864"/>
|
||||||
</requestFiltering>
|
</requestFiltering>
|
||||||
</security>
|
</security>
|
||||||
|
<staticContent>
|
||||||
|
<remove fileExtension=".woff" />
|
||||||
|
<mimeMap fileExtension=".woff" mimeType="application/font-woff" />
|
||||||
|
<remove fileExtension=".js" />
|
||||||
|
<mimeMap fileExtension=".js" mimeType="text/javascript" />
|
||||||
|
<remove fileExtension=".svg" />
|
||||||
|
<mimeMap fileExtension=".svg" mimeType="image/svg+xml" />
|
||||||
|
</staticContent>
|
||||||
</system.webServer>
|
</system.webServer>
|
||||||
|
|
||||||
<runtime>
|
<runtime>
|
||||||
|
|||||||
@@ -15,9 +15,39 @@ namespace Orchard.FileSystems.AppData {
|
|||||||
string Combine(params string[] paths);
|
string Combine(params string[] paths);
|
||||||
|
|
||||||
bool FileExists(string path);
|
bool FileExists(string path);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates or overwrites the file in the specified path with the specified content.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path and name of the file to create.</param>
|
||||||
|
/// <param name="content">The content to write in the created file.</param>
|
||||||
|
/// <remarks>If the folder doesn't exist, it will be created.</remarks>
|
||||||
void CreateFile(string path, string content);
|
void CreateFile(string path, string content);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates or overwrites the file in the specified path.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path and name of the file to create.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A <see cref="Stream"/> that provides read/write access to the file specified in path.
|
||||||
|
/// </returns>
|
||||||
|
/// <remarks>If the folder doesn't exist, it will be created.</remarks>
|
||||||
Stream CreateFile(string path);
|
Stream CreateFile(string path);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens a text file, reads all lines of the file, and then closes the file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path and name of the file to read.</param>
|
||||||
|
/// <returns>A string containing all lines of the file, or <code>null</code> if the file doesn't exist.</returns>
|
||||||
string ReadFile(string path);
|
string ReadFile(string path);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Open an existing file for reading.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path and name of the file to create.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A <see cref="Stream"/> that provides read access to the file specified in path.
|
||||||
|
/// </returns>
|
||||||
Stream OpenFile(string path);
|
Stream OpenFile(string path);
|
||||||
void StoreFile(string sourceFileName, string destinationPath);
|
void StoreFile(string sourceFileName, string destinationPath);
|
||||||
void DeleteFile(string path);
|
void DeleteFile(string path);
|
||||||
|
|||||||
Reference in New Issue
Block a user