mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2026-02-09 09:16:41 +08:00
Merge branch 'issue/8684' into issue/8686-dev
This commit is contained in:
@@ -53,9 +53,9 @@
|
||||
<add extension=".csproj" type="Orchard.Environment.Extensions.Compilers.CSharpExtensionBuildProviderShim"/>
|
||||
</buildProviders>
|
||||
<assemblies>
|
||||
<add assembly="Microsoft.Owin.Host.SystemWeb, Version=4.1.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||
<add assembly="Microsoft.Owin.Host.SystemWeb, Version=4.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||
<add assembly="System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
|
||||
<add assembly="System.Web.Mvc, Version=5.2.7, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
|
||||
<add assembly="System.Web.Mvc, Version=5.2.7, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
|
||||
<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
|
||||
<add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
|
||||
<add assembly="System.Data.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
|
||||
|
||||
@@ -1,18 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Core.Contents.Extensions;
|
||||
using Orchard.Data;
|
||||
using Orchard.Data.Migration;
|
||||
using Orchard.Environment.Configuration;
|
||||
|
||||
namespace Orchard.Core.Common {
|
||||
public class Migrations : DataMigrationImpl {
|
||||
private readonly IRepository<IdentityPartRecord> _identityPartRepository;
|
||||
private readonly ISessionFactoryHolder _sessionFactoryHolder;
|
||||
private readonly ShellSettings _shellSettings;
|
||||
|
||||
private HashSet<string> _existingIndexNames = new HashSet<string>();
|
||||
|
||||
|
||||
public Migrations(IRepository<IdentityPartRecord> identityPartRepository) {
|
||||
public Migrations(
|
||||
IRepository<IdentityPartRecord> identityPartRepository,
|
||||
ISessionFactoryHolder sessionFactoryHolder,
|
||||
ShellSettings shellSettings) {
|
||||
_identityPartRepository = identityPartRepository;
|
||||
_sessionFactoryHolder = sessionFactoryHolder;
|
||||
_shellSettings = shellSettings;
|
||||
}
|
||||
|
||||
|
||||
@@ -155,35 +167,72 @@ namespace Orchard.Core.Common {
|
||||
return 6;
|
||||
}
|
||||
|
||||
// When upgrading from version 6 of 1.10.x (up until version 9), we'll just execute the same steps, but in a
|
||||
// different order.
|
||||
public int UpdateFrom6() {
|
||||
SchemaBuilder.AlterTable(nameof(IdentityPartRecord), table => table
|
||||
.CreateIndex($"IDX_{nameof(IdentityPartRecord)}_{nameof(IdentityPartRecord.Identifier)}", nameof(IdentityPartRecord.Identifier)));
|
||||
// This is the original step of the dev branch.
|
||||
AddIndexForIdentityPartRecordIdentifier();
|
||||
|
||||
return 7;
|
||||
}
|
||||
|
||||
public int UpdateFrom7() {
|
||||
// The Container_Id is basically a foreign key, used in several queries
|
||||
SchemaBuilder.AlterTable(nameof(CommonPartRecord), table => {
|
||||
table.CreateIndex($"IDX_{nameof(CommonPartRecord)}_Container_id",
|
||||
"Container_id");
|
||||
});
|
||||
// This is the original step of the dev branch.
|
||||
AddIndexForCommonPartRecordContainerId();
|
||||
|
||||
// When upgrading from version 7 of 1.10.x, this index isn't created yet, so we need to run this step
|
||||
// "again". On the other hand, AddIndexesForCommonPartOwner in UpdateFrom8 won't do anything, because those
|
||||
// indexes were added in the 1.10.x version of UpdateFrom6.
|
||||
AddIndexForIdentityPartRecordIdentifier();
|
||||
|
||||
return 8;
|
||||
}
|
||||
|
||||
public int UpdateFrom8() {
|
||||
// Studying SQL Server query execution plans we noticed that when the system
|
||||
// tries to find content items for requests such as
|
||||
// "The items of type TTT owned by me, ordered from the most recent"
|
||||
// the existing indexes are not used. SQL Server does an index scan on the
|
||||
// Primary key for CommonPartRecord. This may lead to annoying deadlocks when
|
||||
// there are two concurrent transactions that are doing both this kind of query
|
||||
// as well as an update (or insert) in the CommonPartRecord.
|
||||
// Tests show that this can be easily fixed by adding a non-clustered index
|
||||
// with these keys: OwnerId, {one of PublishedUTC, ModifiedUTC, CreatedUTC}.
|
||||
// That means we need three indexes (one for each DateTime) to support ordering
|
||||
// on either of them.
|
||||
// This is the original step of the dev branch.
|
||||
AddIndexesForCommonPartOwner();
|
||||
|
||||
// When upgrading from version 8 of 1.10.x, this index isn't created yet, so we need to run this step
|
||||
// "again"
|
||||
AddIndexForCommonPartRecordContainerId();
|
||||
|
||||
return 9;
|
||||
}
|
||||
|
||||
// This change was originally UpdateFrom7 on 1.10.x and UpdateFrom6 on dev.
|
||||
private void AddIndexForIdentityPartRecordIdentifier() {
|
||||
var indexName = $"IDX_{nameof(IdentityPartRecord)}_{nameof(IdentityPartRecord.Identifier)}";
|
||||
|
||||
if (IndexExists(nameof(IdentityPartRecord), indexName)) return;
|
||||
|
||||
SchemaBuilder.AlterTable(nameof(IdentityPartRecord), table => table.CreateIndex(
|
||||
indexName,
|
||||
nameof(IdentityPartRecord.Identifier)));
|
||||
|
||||
IndexCreated(nameof(IdentityPartRecord), indexName);
|
||||
}
|
||||
|
||||
// This change was originally UpdateFrom8 on 1.10.x and UpdateFrom7 on dev.
|
||||
private void AddIndexForCommonPartRecordContainerId() {
|
||||
var indexName = $"IDX_{nameof(CommonPartRecord)}_Container_id";
|
||||
|
||||
if (IndexExists(nameof(CommonPartRecord), indexName)) return;
|
||||
|
||||
// Container_Id is used in several queries like a foreign key.
|
||||
SchemaBuilder.AlterTable(nameof(CommonPartRecord), table => table.CreateIndex(indexName, "Container_id"));
|
||||
|
||||
IndexCreated(nameof(CommonPartRecord), indexName);
|
||||
}
|
||||
|
||||
// This change was originally UpdateFrom6 on 1.10.x and UpdateFrom8 on dev.
|
||||
private void AddIndexesForCommonPartOwner() {
|
||||
// Studying SQL Server query execution plans we noticed that when the system tries to find content items for
|
||||
// requests such as "The items of type TTT owned by me, ordered from the most recent" the existing indexes
|
||||
// are not used. SQL Server does an index scan on the Primary key for CommonPartRecord. This may lead to
|
||||
// annoying deadlocks when there are two concurrent transactions that are doing both this kind of query as
|
||||
// well as an update (or insert) in the CommonPartRecord. Tests show that this can be easily fixed by adding
|
||||
// a non-clustered index with these keys: OwnerId, {one of PublishedUTC, ModifiedUTC, CreatedUTC}. That
|
||||
// means we need three indexes (one for each DateTime) to support ordering on either of them.
|
||||
|
||||
// The queries we analyzed look like (in pseudo sql)
|
||||
// SELECT TOP (N) *
|
||||
@@ -198,20 +247,51 @@ namespace Orchard.Core.Common {
|
||||
// and this_.Published = 1
|
||||
// ORDER BY
|
||||
// commonpart2_PublishedUtc desc
|
||||
var createdUtcIndexName = $"IDX_{nameof(CommonPartRecord)}_OwnedBy_ByCreation";
|
||||
var modifiedUtcIndexName = $"IDX_{nameof(CommonPartRecord)}_OwnedBy_ByModification";
|
||||
var publishedUtcIndexName = $"IDX_{nameof(CommonPartRecord)}_OwnedBy_ByPublication";
|
||||
|
||||
if (IndexExists(nameof(CommonPartRecord), createdUtcIndexName)) return;
|
||||
|
||||
SchemaBuilder.AlterTable(nameof(CommonPartRecord), table => {
|
||||
table.CreateIndex($"IDX_{nameof(CommonPartRecord)}_OwnedBy_ByCreation",
|
||||
nameof(CommonPartRecord.OwnerId),
|
||||
nameof(CommonPartRecord.CreatedUtc));
|
||||
table.CreateIndex($"IDX_{nameof(CommonPartRecord)}_OwnedBy_ByModification",
|
||||
nameof(CommonPartRecord.OwnerId),
|
||||
nameof(CommonPartRecord.ModifiedUtc));
|
||||
table.CreateIndex($"IDX_{nameof(CommonPartRecord)}_OwnedBy_ByPublication",
|
||||
nameof(CommonPartRecord.OwnerId),
|
||||
nameof(CommonPartRecord.PublishedUtc));
|
||||
table.CreateIndex(createdUtcIndexName, nameof(CommonPartRecord.OwnerId), nameof(CommonPartRecord.CreatedUtc));
|
||||
table.CreateIndex(modifiedUtcIndexName, nameof(CommonPartRecord.OwnerId), nameof(CommonPartRecord.ModifiedUtc));
|
||||
table.CreateIndex(publishedUtcIndexName, nameof(CommonPartRecord.OwnerId), nameof(CommonPartRecord.PublishedUtc));
|
||||
});
|
||||
|
||||
return 9;
|
||||
IndexCreated(nameof(CommonPartRecord), createdUtcIndexName);
|
||||
IndexCreated(nameof(CommonPartRecord), modifiedUtcIndexName);
|
||||
IndexCreated(nameof(CommonPartRecord), publishedUtcIndexName);
|
||||
}
|
||||
|
||||
private bool IndexExists(string tableName, string indexName) {
|
||||
var tenantTablesPrefix = string.IsNullOrEmpty(_shellSettings.DataTablePrefix)
|
||||
? string.Empty : $"{_shellSettings.DataTablePrefix}_";
|
||||
|
||||
if (!_existingIndexNames.Any()) {
|
||||
// Database-agnostic way of checking the existence of an index.
|
||||
using (var session = _sessionFactoryHolder.GetSessionFactory().OpenSession()) {
|
||||
var connection = session.Connection ?? throw new InvalidOperationException(
|
||||
"The database connection object should derive from DbConnection to check if an index exists.");
|
||||
|
||||
var indexes = connection.GetSchema("Indexes").Rows.Cast<DataRow>();
|
||||
|
||||
if (!string.IsNullOrEmpty(tenantTablesPrefix)) {
|
||||
indexes = indexes.Where(row => row["TABLE_NAME"].ToString().StartsWith(tenantTablesPrefix));
|
||||
}
|
||||
|
||||
_existingIndexNames = indexes.Select(row => $"{row["TABLE_NAME"]}.{row["INDEX_NAME"]}").ToHashSet();
|
||||
}
|
||||
}
|
||||
|
||||
return _existingIndexNames.Contains($"{SchemaBuilder.TableDbName(tableName)}.{tenantTablesPrefix}{indexName}");
|
||||
}
|
||||
|
||||
private void IndexCreated(string tableName, string indexName) {
|
||||
var tenantTablesPrefix = string.IsNullOrEmpty(_shellSettings.DataTablePrefix)
|
||||
? string.Empty : $"{_shellSettings.DataTablePrefix}_";
|
||||
|
||||
_existingIndexNames.Add($"{SchemaBuilder.TableDbName(tableName)}.{tenantTablesPrefix}{indexName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,10 +63,14 @@
|
||||
<Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NHibernate, Version=5.3.0.0, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\NHibernate.5.3.10\lib\net461\NHibernate.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.DataAnnotations">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Web.ApplicationServices" />
|
||||
|
||||
@@ -5,4 +5,5 @@
|
||||
<package id="Microsoft.AspNet.WebPages" version="3.2.7" targetFramework="net48" />
|
||||
<package id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="2.0.1" targetFramework="net48" />
|
||||
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net48" />
|
||||
<package id="NHibernate" version="5.3.10" targetFramework="net48" />
|
||||
</packages>
|
||||
|
||||
@@ -62,11 +62,11 @@
|
||||
<Reference Include="Microsoft.Owin, Version=4.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\Microsoft.Owin.4.2.2\lib\net45\Microsoft.Owin.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Owin.Host.SystemWeb, Version=4.1.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\Microsoft.Owin.Host.SystemWeb.4.1.1\lib\net45\Microsoft.Owin.Host.SystemWeb.dll</HintPath>
|
||||
<Reference Include="Microsoft.Owin.Host.SystemWeb, Version=4.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\Microsoft.Owin.Host.SystemWeb.4.2.2\lib\net45\Microsoft.Owin.Host.SystemWeb.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Owin.Security, Version=4.1.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\Microsoft.Owin.Security.4.1.1\lib\net45\Microsoft.Owin.Security.dll</HintPath>
|
||||
<Reference Include="Microsoft.Owin.Security, Version=4.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\Microsoft.Owin.Security.4.2.2\lib\net45\Microsoft.Owin.Security.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<package id="Microsoft.AspNet.WebPages" version="3.2.7" targetFramework="net48" />
|
||||
<package id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="2.0.1" targetFramework="net48" />
|
||||
<package id="Microsoft.Owin" version="4.2.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Owin.Host.SystemWeb" version="4.1.1" targetFramework="net48" />
|
||||
<package id="Microsoft.Owin.Security" version="4.1.1" targetFramework="net48" />
|
||||
<package id="Microsoft.Owin.Host.SystemWeb" version="4.2.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Owin.Security" version="4.2.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net48" />
|
||||
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net48" />
|
||||
<package id="Owin" version="1.0" targetFramework="net48" />
|
||||
|
||||
@@ -86,11 +86,11 @@
|
||||
<Reference Include="Microsoft.Owin, Version=4.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\Microsoft.Owin.4.2.2\lib\net45\Microsoft.Owin.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Owin.Host.SystemWeb, Version=4.1.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\Microsoft.Owin.Host.SystemWeb.4.1.1\lib\net45\Microsoft.Owin.Host.SystemWeb.dll</HintPath>
|
||||
<Reference Include="Microsoft.Owin.Host.SystemWeb, Version=4.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\Microsoft.Owin.Host.SystemWeb.4.2.2\lib\net45\Microsoft.Owin.Host.SystemWeb.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Owin.Security, Version=4.1.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\Microsoft.Owin.Security.4.1.1\lib\net45\Microsoft.Owin.Security.dll</HintPath>
|
||||
<Reference Include="Microsoft.Owin.Security, Version=4.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\Microsoft.Owin.Security.4.2.2\lib\net45\Microsoft.Owin.Security.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
<package id="Microsoft.AspNet.WebPages" version="3.2.7" targetFramework="net48" />
|
||||
<package id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="2.0.1" targetFramework="net48" />
|
||||
<package id="Microsoft.Owin" version="4.2.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Owin.Host.SystemWeb" version="4.1.1" targetFramework="net48" />
|
||||
<package id="Microsoft.Owin.Security" version="4.1.1" targetFramework="net48" />
|
||||
<package id="Microsoft.Owin.Host.SystemWeb" version="4.2.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Owin.Security" version="4.2.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net48" />
|
||||
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net48" />
|
||||
<package id="NHibernate" version="5.3.10" targetFramework="net48" />
|
||||
|
||||
@@ -62,11 +62,11 @@
|
||||
<Reference Include="Microsoft.Owin, Version=4.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\Microsoft.Owin.4.2.2\lib\net45\Microsoft.Owin.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Owin.Host.SystemWeb, Version=4.1.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\Microsoft.Owin.Host.SystemWeb.4.1.1\lib\net45\Microsoft.Owin.Host.SystemWeb.dll</HintPath>
|
||||
<Reference Include="Microsoft.Owin.Host.SystemWeb, Version=4.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\Microsoft.Owin.Host.SystemWeb.4.2.2\lib\net45\Microsoft.Owin.Host.SystemWeb.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Owin.Security, Version=4.1.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\Microsoft.Owin.Security.4.1.1\lib\net45\Microsoft.Owin.Security.dll</HintPath>
|
||||
<Reference Include="Microsoft.Owin.Security, Version=4.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\Microsoft.Owin.Security.4.2.2\lib\net45\Microsoft.Owin.Security.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
<package id="Microsoft.AspNet.WebPages" version="3.2.7" targetFramework="net48" />
|
||||
<package id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="2.0.1" targetFramework="net48" />
|
||||
<package id="Microsoft.Owin" version="4.2.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Owin.Host.SystemWeb" version="4.1.1" targetFramework="net48" />
|
||||
<package id="Microsoft.Owin.Security" version="4.1.1" targetFramework="net48" />
|
||||
<package id="Microsoft.Owin.Host.SystemWeb" version="4.2.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Owin.Security" version="4.2.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net48" />
|
||||
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net48" />
|
||||
<package id="Owin" version="1.0" targetFramework="net48" />
|
||||
|
||||
@@ -45,6 +45,12 @@ namespace Orchard.MediaLibrary.Handlers {
|
||||
//try to replace items in the field with their translation
|
||||
var itemsInField = _contentManager.GetMany<ContentItem>(field.Ids, VersionOptions.Latest, QueryHints.Empty);
|
||||
var mediaIds = new List<int>();
|
||||
|
||||
// Flag to check if media have been removed from the field.
|
||||
// This happens when the field is set to remove items without localization and one or more media items cannot be localized.
|
||||
// This flag is used to display a model error when the field is required and every media item has been removed from it.
|
||||
var mediaRemoved = false;
|
||||
|
||||
foreach (var item in itemsInField) {
|
||||
// negatives id whoud be localized
|
||||
var mediaItem = _contentManager.Get(item.Id, VersionOptions.Latest);
|
||||
@@ -55,7 +61,7 @@ namespace Orchard.MediaLibrary.Handlers {
|
||||
if (contentCulture == mediaCulture) {
|
||||
// The content culture and the media culture match
|
||||
mediaIds.Add(mediaItem.Id);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (mediaCulture == null) {
|
||||
// The media has not a culture, so it takes the content culture
|
||||
@@ -65,7 +71,7 @@ namespace Orchard.MediaLibrary.Handlers {
|
||||
"{0}: the media item {1} was culture neutral and it has been localized",
|
||||
field.DisplayName,
|
||||
mediaItem.As<MediaPart>().FileName));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// The media has a culture
|
||||
var localizedMedia = _localizationServices.GetLocalizedContentItem(mediaItem, contentCulture);
|
||||
@@ -76,7 +82,7 @@ namespace Orchard.MediaLibrary.Handlers {
|
||||
"{0}: the media item {1} has been replaced by its localized version",
|
||||
field.DisplayName,
|
||||
mediaItem.As<MediaPart>().FileName));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!settings.RemoveItemsWithoutLocalization) {
|
||||
// The media supports translations but have not a localized version, so it will be cloned in the right language
|
||||
@@ -92,33 +98,35 @@ namespace Orchard.MediaLibrary.Handlers {
|
||||
"{0}: a localized version of media item {1} has been created",
|
||||
field.DisplayName,
|
||||
mediaItem.As<MediaPart>().FileName));
|
||||
}
|
||||
}
|
||||
else {
|
||||
_orchardServices.Notifier.Warning(T(
|
||||
"{0}: the media item {1} has been removed from the field because its culture differs from content's culture",
|
||||
field.DisplayName,
|
||||
mediaItem.As<MediaPart>().FileName));
|
||||
mediaRemoved = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (mediaItem != null && !mediaIsLocalizable) {
|
||||
if (!settings.RemoveItemsWithNoLocalizationPart) {
|
||||
mediaIds.Add(mediaItem.Id);
|
||||
}
|
||||
}
|
||||
else {
|
||||
_orchardServices.Notifier.Warning(T(
|
||||
"{0}: the media item {1} has been removed from the field because culture neutral",
|
||||
field.DisplayName,
|
||||
mediaItem.As<MediaPart>().FileName));
|
||||
mediaRemoved = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
field.Ids = mediaIds.Distinct().ToArray();
|
||||
|
||||
if (field.Ids.Length == 0 && fieldSettings.Required) {
|
||||
if (field.Ids.Length == 0 && fieldSettings.Required && mediaRemoved) {
|
||||
context.Updater.AddModelError("Id", T("The {0} field is required.", field.DisplayName));
|
||||
}
|
||||
}
|
||||
@@ -126,4 +134,4 @@ namespace Orchard.MediaLibrary.Handlers {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,8 +108,8 @@
|
||||
<Reference Include="Microsoft.Owin, Version=4.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\Microsoft.Owin.4.2.2\lib\net45\Microsoft.Owin.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Owin.Host.SystemWeb, Version=4.1.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\Microsoft.Owin.Host.SystemWeb.4.1.1\lib\net45\Microsoft.Owin.Host.SystemWeb.dll</HintPath>
|
||||
<Reference Include="Microsoft.Owin.Host.SystemWeb, Version=4.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\Microsoft.Owin.Host.SystemWeb.4.2.2\lib\net45\Microsoft.Owin.Host.SystemWeb.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Owin.Security, Version=4.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\Microsoft.Owin.Security.4.2.2\lib\net45\Microsoft.Owin.Security.dll</HintPath>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<package id="Microsoft.IdentityModel.Protocols.OpenIdConnect" version="5.2.4" targetFramework="net48" />
|
||||
<package id="Microsoft.IdentityModel.Tokens" version="5.2.4" targetFramework="net48" />
|
||||
<package id="Microsoft.Owin" version="4.2.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Owin.Host.SystemWeb" version="4.1.1" targetFramework="net48" />
|
||||
<package id="Microsoft.Owin.Host.SystemWeb" version="4.2.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Owin.Security" version="4.2.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Owin.Security.ActiveDirectory" version="4.0.0" targetFramework="net48" />
|
||||
<package id="Microsoft.Owin.Security.Cookies" version="4.2.2" targetFramework="net48" />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
@@ -7,6 +8,7 @@ using Orchard.Core.Contents.Extensions;
|
||||
using Orchard.Core.Title.Models;
|
||||
using Orchard.Data;
|
||||
using Orchard.Data.Migration;
|
||||
using Orchard.Environment.Configuration;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Projections.Models;
|
||||
|
||||
@@ -15,14 +17,22 @@ namespace Orchard.Projections {
|
||||
private readonly IRepository<MemberBindingRecord> _memberBindingRepository;
|
||||
private readonly IRepository<LayoutRecord> _layoutRepository;
|
||||
private readonly IRepository<PropertyRecord> _propertyRecordRepository;
|
||||
private readonly ISessionFactoryHolder _sessionFactoryHolder;
|
||||
private readonly ShellSettings _shellSettings;
|
||||
|
||||
private HashSet<string> _existingColumnNames = new HashSet<string>();
|
||||
|
||||
public Migrations(
|
||||
IRepository<MemberBindingRecord> memberBindingRepository,
|
||||
IRepository<LayoutRecord> layoutRepository,
|
||||
IRepository<PropertyRecord> propertyRecordRepository) {
|
||||
IRepository<PropertyRecord> propertyRecordRepository,
|
||||
ISessionFactoryHolder sessionFactoryHolder,
|
||||
ShellSettings shellSettings) {
|
||||
_memberBindingRepository = memberBindingRepository;
|
||||
_layoutRepository = layoutRepository;
|
||||
_propertyRecordRepository = propertyRecordRepository;
|
||||
_sessionFactoryHolder = sessionFactoryHolder;
|
||||
_shellSettings = shellSettings;
|
||||
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
@@ -356,32 +366,79 @@ namespace Orchard.Projections {
|
||||
return 5;
|
||||
}
|
||||
|
||||
#pragma warning disable CS0618
|
||||
// disable compiler warning regarding the fact that RewriteOutput is obsolete
|
||||
// because this migration is handling just that.
|
||||
// When upgrading from version 5 of 1.10.x (up until version 7), we'll just execute the same steps, but in a
|
||||
// different order.
|
||||
public int UpdateFrom5() {
|
||||
// This is the original step of the dev branch.
|
||||
MigratePropertyRecordToRewriteOutputCondition();
|
||||
|
||||
return 6;
|
||||
}
|
||||
|
||||
public int UpdateFrom6() {
|
||||
// This is the original step of the dev branch.
|
||||
AddLayoutRecordGuid();
|
||||
|
||||
// When upgrading from version 6 of 1.10.x, this column isn't created yet, so we need to run this step
|
||||
// "again".
|
||||
MigratePropertyRecordToRewriteOutputCondition();
|
||||
|
||||
return 7;
|
||||
}
|
||||
|
||||
// This change was originally UpdateFrom5 on dev (but didn't exist on 1.10.x).
|
||||
private void MigratePropertyRecordToRewriteOutputCondition() {
|
||||
if (ColumnExists("PropertyRecord", "RewriteOutputCondition")) return;
|
||||
|
||||
SchemaBuilder.AlterTable("PropertyRecord", table => table
|
||||
.AddColumn<string>("RewriteOutputCondition", c => c.Unlimited())
|
||||
);
|
||||
|
||||
foreach (var property in _propertyRecordRepository.Table)
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
// Reading this obsolete property to migrate its data to a new one.
|
||||
if (property.RewriteOutput) property.RewriteOutputCondition = "true";
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
return 6;
|
||||
ColumnAdded("PropertyRecord", "RewriteOutputCondition");
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
|
||||
public int UpdateFrom6() {
|
||||
SchemaBuilder.AlterTable("LayoutRecord", t => t.AddColumn<string>("GUIdentifier",
|
||||
column => column.WithLength(68)));
|
||||
// This change was originally UpdateFrom5 on 1.10.x and UpdateFrom6 on dev.
|
||||
private void AddLayoutRecordGuid() {
|
||||
if (ColumnExists("LayoutRecord", "GUIdentifier")) return;
|
||||
|
||||
SchemaBuilder.AlterTable("LayoutRecord", table =>
|
||||
table.AddColumn<string>("GUIdentifier", column => column.WithLength(68)));
|
||||
|
||||
var layoutRecords = _layoutRepository.Table.Where(l => l.GUIdentifier == null || l.GUIdentifier == "").ToList();
|
||||
foreach (var layout in layoutRecords) {
|
||||
layout.GUIdentifier = Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
return 7;
|
||||
ColumnAdded("LayoutRecord", "GUIdentifier");
|
||||
}
|
||||
|
||||
private bool ColumnExists(string tableName, string columnName) {
|
||||
if (!_existingColumnNames.Any()) {
|
||||
// Database-agnostic way of checking the existence of a column.
|
||||
using (var session = _sessionFactoryHolder.GetSessionFactory().OpenSession()) {
|
||||
var connection = session.Connection ?? throw new InvalidOperationException(
|
||||
"The database connection object should derive from DbConnection to check if a column exists.");
|
||||
|
||||
var columns = connection.GetSchema("Columns").Rows.Cast<DataRow>();
|
||||
|
||||
if (!string.IsNullOrEmpty(_shellSettings.DataTablePrefix)) {
|
||||
columns = columns.Where(row => row["TABLE_NAME"].ToString().StartsWith($"{_shellSettings.DataTablePrefix}_"));
|
||||
}
|
||||
|
||||
_existingColumnNames = columns.Select(row => $"{row["TABLE_NAME"]}.{row["COLUMN_NAME"]}").ToHashSet();
|
||||
}
|
||||
}
|
||||
|
||||
return _existingColumnNames.Contains($"{SchemaBuilder.TableDbName(tableName)}.{columnName}");
|
||||
}
|
||||
|
||||
private void ColumnAdded(string tableName, string columnName) =>
|
||||
_existingColumnNames.Add($"{SchemaBuilder.TableDbName(tableName)}.{columnName}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace TinyMce.Settings {
|
||||
}
|
||||
|
||||
public override IEnumerable<TemplateViewModel> PartFieldEditorUpdate(ContentPartFieldDefinitionBuilder builder, IUpdateModel updateModel) {
|
||||
if (!_contentLinksDependenciesEnabled || !_htmlFields.Any(x => x.Equals(builder.Name, StringComparison.InvariantCultureIgnoreCase)))
|
||||
if (!_contentLinksDependenciesEnabled || !_htmlFields.Any(x => x.Equals(builder.FieldType, StringComparison.InvariantCultureIgnoreCase)))
|
||||
yield break;
|
||||
|
||||
var model = new ContentLinksSettings();
|
||||
@@ -56,4 +56,4 @@ namespace TinyMce.Settings {
|
||||
yield return DefinitionTemplate(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,8 +73,8 @@
|
||||
<Reference Include="Microsoft.Owin, Version=4.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Owin.4.2.2\lib\net45\Microsoft.Owin.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Owin.Host.SystemWeb, Version=4.1.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Owin.Host.SystemWeb.4.1.1\lib\net45\Microsoft.Owin.Host.SystemWeb.dll</HintPath>
|
||||
<Reference Include="Microsoft.Owin.Host.SystemWeb, Version=4.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Owin.Host.SystemWeb.4.2.2\lib\net45\Microsoft.Owin.Host.SystemWeb.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<add extension=".csproj" type="Orchard.Environment.Extensions.Compilers.CSharpExtensionBuildProviderShim" />
|
||||
</buildProviders>
|
||||
<assemblies>
|
||||
<add assembly="Microsoft.Owin.Host.SystemWeb, Version=4.1.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||
<add assembly="Microsoft.Owin.Host.SystemWeb, Version=4.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||
<add assembly="System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
|
||||
<add assembly="System.Web.Mvc, Version=5.2.7, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
|
||||
<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
|
||||
@@ -243,7 +243,7 @@
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Owin.Host.SystemWeb" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.2.2.0" newVersion="4.2.2.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.IdentityModel.Tokens.Jwt" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<package id="Microsoft.AspNet.WebPages" version="3.2.7" targetFramework="net48" />
|
||||
<package id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="2.0.1" targetFramework="net48" />
|
||||
<package id="Microsoft.Owin" version="4.2.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Owin.Host.SystemWeb" version="4.1.1" targetFramework="net48" />
|
||||
<package id="Microsoft.Owin.Host.SystemWeb" version="4.2.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net48" />
|
||||
<package id="MySql.Data" version="6.7.9" targetFramework="net48" />
|
||||
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net48" />
|
||||
|
||||
18
src/Orchard/Data/MapAsRecordAttribute.cs
Normal file
18
src/Orchard/Data/MapAsRecordAttribute.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace Orchard.Data {
|
||||
/// <summary>
|
||||
/// Marks whether a class should be mapped as an NHibernate record
|
||||
/// </summary>
|
||||
public class MapAsRecordAttribute : Attribute {
|
||||
private readonly bool _enabled;
|
||||
|
||||
public MapAsRecordAttribute() : this(true) { }
|
||||
|
||||
public MapAsRecordAttribute(bool enabled) {
|
||||
_enabled = enabled;
|
||||
}
|
||||
|
||||
public bool Enabled => _enabled;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using System.Web.Mvc;
|
||||
using Autofac.Core;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Records;
|
||||
using Orchard.Data;
|
||||
using Orchard.Environment.Configuration;
|
||||
using Orchard.Environment.Descriptor.Models;
|
||||
using Orchard.Environment.Extensions;
|
||||
@@ -96,7 +97,7 @@ namespace Orchard.Environment.ShellBuilders {
|
||||
}
|
||||
|
||||
var feature = availableFeatures[shellFeature];
|
||||
|
||||
|
||||
foreach (var childDependency in ExpandDependenciesInternal(availableFeatures, feature.Dependencies, dependentFeatureDescriptor: feature))
|
||||
yield return childDependency;
|
||||
|
||||
@@ -197,7 +198,12 @@ namespace Orchard.Environment.ShellBuilders {
|
||||
}
|
||||
|
||||
private static bool IsRecord(Type type) {
|
||||
return ((type.Namespace ?? "").EndsWith(".Models") || (type.Namespace ?? "").EndsWith(".Records")) &&
|
||||
var mapAsRecordAttr = type.GetCustomAttributes(typeof(MapAsRecordAttribute), false)
|
||||
.OfType<MapAsRecordAttribute>()
|
||||
.FirstOrDefault();
|
||||
|
||||
return ((type.Namespace ?? "").EndsWith(".Models") || (type.Namespace ?? "").EndsWith(".Records") || mapAsRecordAttr?.Enabled == true) &&
|
||||
mapAsRecordAttr?.Enabled != false &&
|
||||
type.GetProperty("Id") != null &&
|
||||
(type.GetProperty("Id").GetAccessors()).All(x => x.IsVirtual) &&
|
||||
!type.IsSealed &&
|
||||
|
||||
@@ -62,12 +62,36 @@ namespace Orchard.Localization.Services {
|
||||
return _workContextAccessor.GetContext().CurrentCulture;
|
||||
}
|
||||
|
||||
protected Dictionary<int, CultureRecord> GetAllCulturesById() {
|
||||
return _cacheManager.Get("all_culture_records_by_id", true, context => {
|
||||
context.Monitor(_signals.When("culturesChanged"));
|
||||
|
||||
return _cultureRepository.Table
|
||||
.ToDictionary(cr => cr.Id);
|
||||
});
|
||||
}
|
||||
public CultureRecord GetCultureById(int id) {
|
||||
return _cultureRepository.Get(id);
|
||||
var cultures = GetAllCulturesById();
|
||||
CultureRecord result;
|
||||
cultures.TryGetValue(id, out result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected Dictionary<string, CultureRecord> GetAllCulturesByName() {
|
||||
return _cacheManager.Get("all_culture_records_by_name", true, context => {
|
||||
context.Monitor(_signals.When("culturesChanged"));
|
||||
|
||||
return _cultureRepository.Table
|
||||
.ToDictionary(cr => cr.Culture);
|
||||
});
|
||||
}
|
||||
public CultureRecord GetCultureByName(string cultureName) {
|
||||
return _cultureRepository.Get(cr => cr.Culture == cultureName);
|
||||
var cultures = GetAllCulturesByName();
|
||||
CultureRecord result;
|
||||
cultures.TryGetValue(cultureName, out result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public string GetSiteCulture() {
|
||||
|
||||
@@ -157,6 +157,7 @@
|
||||
<Compile Include="ContentManagement\Extensions\DriverResultExtensions.cs" />
|
||||
<Compile Include="ContentManagement\Handlers\CloneContentContext.cs" />
|
||||
<Compile Include="ContentManagement\IGlobalCriteriaProvider.cs" />
|
||||
<Compile Include="Data\MapAsRecordAttribute.cs" />
|
||||
<Compile Include="Data\Migration\Interpreters\PostgreSqlCommandInterpreter.cs" />
|
||||
<Compile Include="Data\NoLockInterceptor.cs" />
|
||||
<Compile Include="Data\Providers\DefaultNoLockTableProvider.cs" />
|
||||
|
||||
Reference in New Issue
Block a user