Merge branch '1.10.x' into 1.10.x-to-dev

# Conflicts:
#	src/Orchard.Web/Core/Contents/Controllers/AdminController.cs
#	src/Orchard.Web/Core/Contents/Drivers/ContentsDriver.cs
#	src/Orchard.Web/Core/Contents/Placement.info
#	src/Orchard.Web/Modules/Orchard.Fields/Orchard.Fields.csproj
#	src/Orchard.Web/Themes/TheAdmin/Styles/site.css
#	src/Orchard/Orchard.Framework.csproj
This commit is contained in:
Benedek Farkas
2025-09-18 22:35:24 +02:00
26 changed files with 1332 additions and 905 deletions

View File

@@ -362,6 +362,32 @@ namespace Orchard.Core.Contents.Controllers {
});
}
/// <summary>
/// This action is specific to the submit button of the edit form.
/// Unpublish logic is the same of the Unpublish action, which is called by the content list.
/// </summary>
/// <param name="id"></param>
/// <param name="returnUrl"></param>
/// <returns></returns>
[HttpPost, ActionName("Edit")]
[Mvc.FormValueRequired("submit.Unpublish")]
public ActionResult EditUnpublishPOST(int id, string returnUrl) {
return Unpublish(id, returnUrl);
}
/// <summary>
/// This action is specific to the submit button of the edit form.
/// Delete logic is the same of the Remove action, which is called by the content list.
/// </summary>
/// <param name="id"></param>
/// <param name="returnUrl"></param>
/// <returns></returns>
[HttpPost, ActionName("Edit")]
[Mvc.FormValueRequired("submit.Delete")]
public ActionResult EditDeletePOST(int id, string returnUrl) {
return Remove(id, returnUrl);
}
private ActionResult EditPOST(int id, string returnUrl, Func<ContentItem, bool> conditionallyPublish) {
var contentItem = _contentManager.Get(id, VersionOptions.DraftRequired);

View File

@@ -17,12 +17,17 @@ namespace Orchard.Core.Contents.Drivers {
}
protected override DriverResult Editor(ContentPart part, dynamic shapeHelper) {
var results = new List<DriverResult>();
var results = new List<DriverResult> { ContentShape("Content_SaveButton", saveButton => saveButton) };
if (part.TypeDefinition.Settings.GetModel<ContentTypeSettings>().Draftable)
results.Add(ContentShape("Content_SaveButton", saveButton => saveButton));
if (part.TypeDefinition.Settings.GetModel<ContentTypeSettings>().Draftable) {
results.Add(ContentShape("Content_PublishButton", publishButton => publishButton));
results.Add(ContentShape("Content_UnpublishButton", unpublishButton => unpublishButton));
}
results.Add(ContentShape("Content_PublishButton", publishButton => publishButton));
if (part.Id > 0) {
results.Add(ContentShape("Content_DeleteButton", deleteButton => deleteButton));
}
results.Add(ContentShape("Content_CancelButton", cancelButton => cancelButton));
return Combined(results.ToArray());

View File

@@ -7,8 +7,10 @@
-->
<!-- edit "shape" -->
<Place Content_SaveButton="Sidebar:23"/>
<Place Content_PublishButton="Sidebar:24"/>
<Place Content_CancelButton="Sidebar:26"/>
<Place Content_PublishButton="Sidebar:24"/>
<Place Content_UnpublishButton="Sidebar:25"/>
<Place Content_DeleteButton="Sidebar:26"/>
<Place Content_CancelButton="Sidebar:27"/>
<Match DisplayType="Detail">
<Place Parts_Contents_Publish="Content:5"/>
</Match>

View File

@@ -0,0 +1,8 @@
@using Orchard.ContentManagement;
@using Orchard.Core.Contents;
@if (Authorizer.Authorize(Permissions.DeleteContent, (IContent)Model.ContentItem)) {
<fieldset class="delete-button">
<button type="submit" name="submit.Delete" value="submit.Delete" itemprop="RemoveUrl">@T("Delete")</button>
</fieldset>
}

View File

@@ -0,0 +1,12 @@
@using Orchard.ContentManagement;
@using Orchard.Core.Contents;
@{
var contentItem = Model.ContentItem as IContent;
}
@if (Authorizer.Authorize(Permissions.PublishContent, contentItem) && contentItem.IsPublished()) {
<fieldset class="unpublish-button">
<button type="submit" name="submit.Unpublish" value="submit.Unpublish">@T("Unpublish")</button>
</fieldset>
}

View File

@@ -629,12 +629,16 @@
<Content Include="Contents\Views\Parts.Contents.Clone.SummaryAdmin.cshtml" />
<Content Include="Navigation\Views\Admin\Edit.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Contents\Views\Content.UnpublishButton.cshtml" />
<Content Include="Contents\Views\Content.DeleteButton.cshtml" />
<None Include="packages.config" />
<Content Include="Title\Views\DefinitionTemplates\TitlePartSettings.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Containers\Styles\Web.config">
<SubType>Designer</SubType>
</Content>
<None Include="packages.config" />
<Content Include="Title\Views\DefinitionTemplates\TitlePartSettings.cshtml" />
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>

View File

@@ -1,6 +1,5 @@
using System;
using System.Linq;
using System.Reflection;
using System.Web.Mvc;
using Orchard.Blogs.Extensions;
using Orchard.Blogs.Models;
@@ -138,6 +137,12 @@ namespace Orchard.Blogs.Controllers {
});
}
[HttpPost, ActionName("Edit")]
[Mvc.FormValueRequired("submit.Delete")]
public ActionResult EditDeletePOST(int blogId, int postId, string returnUrl) {
return Delete(blogId, postId);
}
[HttpPost, ActionName("Edit")]
[FormValueRequired("submit.Publish")]
public ActionResult EditAndPublishPOST(int blogId, int postId, string returnUrl) {
@@ -156,6 +161,12 @@ namespace Orchard.Blogs.Controllers {
return EditPOST(blogId, postId, returnUrl, contentItem => Services.ContentManager.Publish(contentItem));
}
[HttpPost, ActionName("Edit")]
[Mvc.FormValueRequired("submit.Unpublish")]
public ActionResult EditUnpublishPOST(int blogId, int postId, string returnUrl) {
return Unpublish(blogId, postId);
}
public ActionResult EditPOST(int blogId, int postId, string returnUrl, Action<ContentItem> conditionallyPublish) {
var blog = _blogService.Get(blogId, VersionOptions.Latest);
if (blog == null)

View File

@@ -8,4 +8,8 @@ namespace Orchard.Caching.Services {
void Remove(string key);
void Clear();
}
public interface ICacheStorageProviderWithKeyPrefix : ICacheStorageProvider {
void Clear(string key);
}
}

View File

@@ -4,41 +4,46 @@ using Orchard.ContentManagement.Handlers;
using Orchard.Fields.Fields;
using Orchard.Fields.Settings;
using Orchard.Localization;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Orchard.Fields.Drivers {
public class EnumerationFieldDriver : ContentFieldDriver<EnumerationField> {
public IOrchardServices Services { get; set; }
private const string TemplateName = "Fields/Enumeration.Edit";
public EnumerationFieldDriver(IOrchardServices services) {
Services = services;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
private static string GetPrefix(ContentField field, ContentPart part) =>
part.PartDefinition.Name + "." + field.Name;
private static string GetPrefix(ContentField field, ContentPart part) {
return part.PartDefinition.Name + "." + field.Name;
}
private static string GetDifferentiator(EnumerationField field) => field.Name;
private static string GetDifferentiator(EnumerationField field, ContentPart part) {
return field.Name;
}
protected override DriverResult Display(ContentPart part, EnumerationField field, string displayType, dynamic shapeHelper) {
return ContentShape("Fields_Enumeration", GetDifferentiator(field), () => shapeHelper.Fields_Enumeration());
return ContentShape("Fields_Enumeration", GetDifferentiator(field, part),
() => shapeHelper.Fields_Enumeration());
}
protected override DriverResult Editor(ContentPart part, EnumerationField field, dynamic shapeHelper) {
return ContentShape("Fields_Enumeration_Edit", GetDifferentiator(field), () => {
if (part.IsNew() && string.IsNullOrEmpty(field.Value)) {
var settings = field.PartFieldDefinition.Settings.GetModel<EnumerationFieldSettings>();
if (!string.IsNullOrWhiteSpace(settings.DefaultValue)) {
field.SelectedValues = new string[] { settings.DefaultValue };
return ContentShape("Fields_Enumeration_Edit", GetDifferentiator(field, part),
() => {
if (part.IsNew() && String.IsNullOrEmpty(field.Value)) {
var settings = field.PartFieldDefinition.Settings.GetModel<EnumerationFieldSettings>();
if (!String.IsNullOrWhiteSpace(settings.DefaultValue)) {
field.Value = settings.DefaultValue;
}
}
}
return shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: field, Prefix: GetPrefix(field, part));
});
return shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: field, Prefix: GetPrefix(field, part));
});
}
protected override DriverResult Editor(ContentPart part, EnumerationField field, IUpdateModel updater, dynamic shapeHelper) {

View File

@@ -7,16 +7,28 @@ namespace Orchard.Fields.Fields {
private const char Separator = ';';
public string Value {
get => Storage.Get<string>()?.Trim(Separator) ?? "";
set => Storage.Set(string.IsNullOrWhiteSpace(value)
? string.Empty
// It is now the responsibility of this field to (re-)add the separators.
: Separator + value.Trim(Separator) + Separator);
get { return Storage.Get<string>(); }
set { Storage.Set(value ?? String.Empty); }
}
public string[] SelectedValues {
get => Value?.Split(new[] { Separator }, StringSplitOptions.RemoveEmptyEntries) ?? new string[0];
set => Value = value?.Length > 0 ? string.Join(Separator.ToString(), value) : "";
get {
var value = Value;
if(string.IsNullOrWhiteSpace(value)) {
return new string[0];
}
return value.Split(new [] { Separator }, StringSplitOptions.RemoveEmptyEntries);
}
set {
if (value == null || value.Length == 0) {
Value = String.Empty;
}
else {
Value = Separator + string.Join(Separator.ToString(), value) + Separator;
}
}
}
}
}

View File

@@ -169,7 +169,6 @@
<Compile Include="ViewModels\NumericFieldViewModel.cs" />
<Compile Include="ViewModels\DateTimeFieldViewModel.cs" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Content Include="Views\Fields\Input.cshtml" />
</ItemGroup>
@@ -220,4 +219,4 @@
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
</Project>
</Project>

View File

@@ -4,30 +4,48 @@
@{
var settings = Model.PartFieldDefinition.Settings.GetModel<EnumerationFieldSettings>();
string[] options = (!String.IsNullOrWhiteSpace(settings.Options)) ? settings.Options.Split(new string[] { System.Environment.NewLine }, StringSplitOptions.None) : new string[] { T("Select an option").ToString() };
string[] options = (!String.IsNullOrWhiteSpace(settings.Options)) ?
settings.Options.Split(new string[] { System.Environment.NewLine }, StringSplitOptions.None)
: new string[] { T("Select an option").ToString() };
}
<fieldset>
<label for="@Html.FieldIdFor(m => m.Value)" @if (settings.Required) { <text> class="required" </text> }>@Model.DisplayName</label>
@switch (settings.ListMode) {
case ListMode.Dropdown:
@Html.DropDownListFor(m => m.Value, new SelectList(options, Model.SelectedValues.FirstOrDefault()), settings.Required ? new { required = "required" } : null)
@Html.DropDownListFor(
m => m.Value,
new SelectList(options, Model.SelectedValues.FirstOrDefault()),
settings.Required ? new { required = "required" } : null)
break;
case ListMode.Radiobutton:
foreach (var option in options) {
if (string.IsNullOrWhiteSpace(option)) {
<label>@Html.RadioButton("Value", "", string.IsNullOrWhiteSpace(Model.SelectedValues.FirstOrDefault()), settings.Required ? new { required = "required" } : null)<i>@T("unset")</i></label>
<label>@Html.RadioButton(
"Value",
"",
string.IsNullOrWhiteSpace(Model.SelectedValues.FirstOrDefault()),
settings.Required ? new { required = "required" } : null)<i>@T("unset")</i>
</label>
}
else {
<label>@Html.RadioButton("Value", option, (option == Model.SelectedValues.FirstOrDefault()), settings.Required ? new { required = "required" } : null)@option</label>
<label>@Html.RadioButton(
"Value",
option,
option == Model.SelectedValues.FirstOrDefault(),
settings.Required ? new { required = "required" } : null)@option
</label>
}
}
break;
case ListMode.Listbox:
<input name="@Html.FieldNameFor(m => m.SelectedValues)" type="hidden" />
@Html.ListBoxFor(m => m.SelectedValues, new MultiSelectList(options, Model.SelectedValues), settings.Required ? new { required = "required" } : null)
@Html.ListBoxFor(
m => m.SelectedValues,
new MultiSelectList(options, Model.SelectedValues),
settings.Required ? new { required = "required" } : null)
break;
case ListMode.Checkbox:
@@ -37,7 +55,9 @@
index++;
if (!string.IsNullOrWhiteSpace(option)) {
<div>
<input type="checkbox" name="@Html.FieldNameFor(m => m.SelectedValues)" value="@option" @((Model.SelectedValues != null && Model.SelectedValues.Contains(option)) ? "checked=\"checked\"" : "") class="check-box" id="@Html.FieldIdFor(m => m.SelectedValues)-@index" />
<input type="checkbox" name="@Html.FieldNameFor(m => m.SelectedValues)" value="@option"
@((Model.SelectedValues != null && Model.SelectedValues.Contains(option)) ? "checked=\"checked\"" : "")
class="check-box" id="@Html.FieldIdFor(m => m.SelectedValues)-@index" />
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.SelectedValues)-@index">@option</label>
</div>
}

View File

@@ -1106,17 +1106,17 @@ html.dyn #submit-pager, html.dyn .apply-bulk-actions-auto { display:none; }
padding:0;
}
fieldset.publish-button, fieldset.delete-button, fieldset.save-button {
fieldset.publish-button, fieldset.delete-button, fieldset.save-button, fieldset.unpublish-button {
clear:none;
float:left;
}
fieldset.save-button {
clear:left;
}
fieldset.publish-button {
fieldset.publish-button, fieldset.unpublish-button {
margin: 0 12px 0 0;
padding: 0 12px;
border-right:1px solid #ccc;
border-right: 1px solid #ccc;
}
fieldset.delete-button {
margin: 0 0 0 12px;

View File

@@ -17,12 +17,16 @@ namespace Orchard.MediaLibrary.Controllers {
[Admin, Themed(false)]
public class OEmbedController : Controller {
private readonly IMediaLibraryService _mediaLibraryService;
private readonly IOEmbedService _oEmbedService;
public OEmbedController(
IOrchardServices services,
IMediaLibraryService mediaManagerService) {
_mediaLibraryService = mediaManagerService;
IMediaLibraryService mediaManagerService,
IOEmbedService oEmbedService) {
Services = services;
_mediaLibraryService = mediaManagerService;
_oEmbedService = oEmbedService;
T = NullLocalizer.Instance;
}
@@ -78,39 +82,9 @@ namespace Orchard.MediaLibrary.Controllers {
}
}
var webClient = new WebClient { Encoding = Encoding.UTF8 };
try {
// <link rel="alternate" href="http://vimeo.com/api/oembed.xml?url=http%3A%2F%2Fvimeo.com%2F23608259" type="text/xml+oembed">
viewModel.Content = _oEmbedService.DownloadMediaData(url);
var source = webClient.DownloadString(url);
// seek type="text/xml+oembed" or application/xml+oembed
var oembedSignature = source.IndexOf("type=\"text/xml+oembed\"", StringComparison.OrdinalIgnoreCase);
if (oembedSignature == -1) {
oembedSignature = source.IndexOf("type=\"application/xml+oembed\"", StringComparison.OrdinalIgnoreCase);
}
if (oembedSignature != -1) {
var tagStart = source.Substring(0, oembedSignature).LastIndexOf('<');
var tagEnd = source.IndexOf('>', oembedSignature);
var tag = source.Substring(tagStart, tagEnd - tagStart);
var matches = new Regex("href=\"([^\"]+)\"").Matches(tag);
if (matches.Count > 0) {
var href = matches[0].Groups[1].Value;
try {
var content = webClient.DownloadString(Server.HtmlDecode(href));
viewModel.Content = XDocument.Parse(content);
}
catch {
// bubble exception
}
}
}
if (viewModel.Content == null) {
viewModel.Content = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement("oembed")
);
}
var root = viewModel.Content.Root;
if (!String.IsNullOrWhiteSpace(url)) {
root.El("url", url);

View File

@@ -208,7 +208,9 @@
<Compile Include="Security\MediaAuthorizationEventHandler.cs" />
<Compile Include="Services\IMediaLibraryService.cs" />
<Compile Include="Providers\IMediaFolderProvider.cs" />
<Compile Include="Services\IOEmbedService.cs" />
<Compile Include="Services\MediaLibraryService.cs" />
<Compile Include="Services\OEmbedService.cs" />
<Compile Include="Services\Shapes.cs" />
<Compile Include="Services\XmlRpcHandler.cs" />
<Compile Include="Settings\MediaLibraryPickerFieldLocalizationEditorEvents.cs" />

View File

@@ -0,0 +1,7 @@
using System.Xml.Linq;
namespace Orchard.MediaLibrary.Services {
public interface IOEmbedService : IDependency {
XDocument DownloadMediaData(string url);
}
}

View File

@@ -0,0 +1,51 @@
using System;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Xml.Linq;
namespace Orchard.MediaLibrary.Services {
public class OEmbedService : IOEmbedService {
public XDocument DownloadMediaData(string url) {
var webClient = new WebClient { Encoding = Encoding.UTF8 };
XDocument doc = null;
try {
// <link rel="alternate" href="http://vimeo.com/api/oembed.xml?url=http%3A%2F%2Fvimeo.com%2F23608259" type="text/xml+oembed">
var source = webClient.DownloadString(url);
// seek type="text/xml+oembed" or application/xml+oembed
var oembedSignature = source.IndexOf("type=\"text/xml+oembed\"", StringComparison.OrdinalIgnoreCase);
if (oembedSignature == -1) {
oembedSignature = source.IndexOf("type=\"application/xml+oembed\"", StringComparison.OrdinalIgnoreCase);
}
if (oembedSignature != -1) {
var tagStart = source.Substring(0, oembedSignature).LastIndexOf('<');
var tagEnd = source.IndexOf('>', oembedSignature);
var tag = source.Substring(tagStart, tagEnd - tagStart);
var matches = new Regex("href=\"([^\"]+)\"").Matches(tag);
if (matches.Count > 0) {
var href = matches[0].Groups[1].Value;
try {
var content = webClient.DownloadString(HttpUtility.HtmlDecode(href));
doc = XDocument.Parse(content);
} catch {
// bubble exception
}
}
}
} catch {
doc = null;
}
if (doc == null) {
doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement("oembed")
);
}
return doc;
}
}
}

View File

@@ -9,10 +9,9 @@ using StackExchange.Redis;
using System;
namespace Orchard.Redis.Caching {
[OrchardFeature("Orchard.Redis.Caching")]
[OrchardSuppressDependency("Orchard.Caching.Services.DefaultCacheStorageProvider")]
public class RedisCacheStorageProvider : Component, ICacheStorageProvider {
public class RedisCacheStorageProvider : Component, ICacheStorageProviderWithKeyPrefix {
public const string ConnectionStringKey = "Orchard.Redis.Cache";
private readonly ShellSettings _shellSettings;
@@ -61,6 +60,10 @@ namespace Orchard.Redis.Caching {
_connectionMultiplexer.KeyDeleteWithPrefix(GetLocalizedKey("*"));
}
public void Clear(string key) {
_connectionMultiplexer.KeyDeleteWithPrefix(GetLocalizedKey($"{_shellSettings.Name}:{key}"));
}
private string GetLocalizedKey(string key) {
return _shellSettings.Name + ":Cache:" + key;
}

View File

@@ -33,30 +33,25 @@ namespace Orchard.Widgets.Drivers {
() => shapeHelper.EditorTemplate(TemplateName: "Parts.Widgets.LayerPart", Model: layerPart, Prefix: Prefix))
};
if (layerPart.Id > 0)
results.Add(ContentShape("Widget_DeleteButton",
deleteButton => deleteButton));
return Combined(results.ToArray());
}
protected override DriverResult Editor(LayerPart layerPart, IUpdateModel updater, dynamic shapeHelper) {
if(updater.TryUpdateModel(layerPart, Prefix, null, null)) {
if (updater.TryUpdateModel(layerPart, Prefix, null, null)) {
if (String.IsNullOrWhiteSpace(layerPart.LayerRule)) {
layerPart.LayerRule = "true";
}
if (_widgetsService.GetLayers()
.Any(l =>
.Any(l =>
l.Id != layerPart.Id
&& String.Equals(l.Name, layerPart.Name, StringComparison.InvariantCultureIgnoreCase))) {
&& String.Equals(l.Name, layerPart.Name, StringComparison.InvariantCultureIgnoreCase))) {
updater.AddModelError("Name", T("A Layer with the same name already exists"));
}
try {
_conditionManager.Matches(layerPart.LayerRule);
}
catch (Exception e) {
} catch (Exception e) {
updater.AddModelError("Description", T("The rule is not valid: {0}", e.Message));
}
}
@@ -82,7 +77,7 @@ namespace Orchard.Widgets.Drivers {
var rule = context.Attribute(part.PartDefinition.Name, "LayerRule");
if (rule != null) {
part.LayerRule = rule;
part.LayerRule = rule;
}
}

View File

@@ -35,26 +35,22 @@ namespace Orchard.Widgets.Drivers {
() => shapeHelper.EditorTemplate(TemplateName: "Parts.Widgets.WidgetPart", Model: widgetPart, Prefix: Prefix))
};
if (widgetPart.Id > 0)
results.Add(ContentShape("Widget_DeleteButton",
deleteButton => deleteButton));
return Combined(results.ToArray());
}
protected override DriverResult Editor(WidgetPart widgetPart, IUpdateModel updater, dynamic shapeHelper) {
updater.TryUpdateModel(widgetPart, Prefix, null, null);
if(string.IsNullOrWhiteSpace(widgetPart.Title)) {
if (string.IsNullOrWhiteSpace(widgetPart.Title)) {
updater.AddModelError("Title", T("Title can't be empty."));
}
// if there is a name, ensure it's unique
if(!string.IsNullOrWhiteSpace(widgetPart.Name)) {
if (!string.IsNullOrWhiteSpace(widgetPart.Name)) {
widgetPart.Name = widgetPart.Name.ToHtmlName();
var widgets = _contentManager.Query<WidgetPart, WidgetPartRecord>().Where(x => x.Name == widgetPart.Name && x.Id != widgetPart.Id).Count();
if(widgets > 0) {
if (widgets > 0) {
updater.AddModelError("Name", T("A Widget with the same Name already exists."));
}
}

View File

@@ -208,9 +208,6 @@
<Content Include="Views\EditorTemplates\Parts.Widgets.LayerPart.cshtml" />
<Content Include="Views\EditorTemplates\Parts.Widgets.WidgetPart.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Widget.DeleteButton.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Web.config" />
</ItemGroup>

View File

@@ -1,5 +1,4 @@
<Placement>
<Place Widget_DeleteButton="Sidebar:25"/>
<Place Parts_Widgets_LayerPart="Content:1"/>
<Place Parts_Widgets_WidgetPart="Content:1"/>
</Placement>

View File

@@ -1,3 +0,0 @@
<fieldset class="delete-button">
<button type="submit" name="submit.Delete" value="@T("Delete")" itemprop="RemoveUrl">@T("Delete")</button>
</fieldset>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,16 @@
using System;
using FluentNHibernate.Conventions;
using FluentNHibernate.Conventions.Instances;
namespace Orchard.Data.Conventions
{
[AttributeUsage(AttributeTargets.Property)]
public class LazyLoadAttribute : Attribute {
}
public class LazyLoadConvention : AttributePropertyConvention<LazyLoadAttribute> {
protected override void Apply(LazyLoadAttribute attribute, IPropertyInstance instance) {
instance.LazyLoad();
}
}
}

View File

@@ -154,6 +154,7 @@
<Compile Include="ContentManagement\Extensions\DriverResultExtensions.cs" />
<Compile Include="ContentManagement\Handlers\CloneContentContext.cs" />
<Compile Include="ContentManagement\IGlobalCriteriaProvider.cs" />
<Compile Include="Data\Conventions\LazyLoadConvention.cs" />
<Compile Include="Data\MapAsRecordAttribute.cs" />
<Compile Include="Data\Migration\Interpreters\PostgreSqlCommandInterpreter.cs" />
<Compile Include="Data\NoLockInterceptor.cs" />