Merge remote-tracking branch 'origin/v8/dev' into netcore/dev

# Conflicts:
#	src/Umbraco.Core/IO/SystemDirectories.cs
#	src/Umbraco.Core/Udi.cs
#	src/Umbraco.Core/Umbraco.Core.csproj
This commit is contained in:
Bjarke Berg
2020-02-07 10:01:38 +01:00
44 changed files with 623 additions and 211 deletions
@@ -0,0 +1,63 @@
using NPoco;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0.Models
{
/// <summary>
/// Snapshot of the <see cref="ContentTypeDto"/> as it was at version 8.0
/// </summary>
/// <remarks>
/// This is required during migrations the schema of this table changed and running SQL against the new table would result in errors
/// </remarks>
[TableName(TableName)]
[PrimaryKey("pk")]
[ExplicitColumns]
internal class ContentTypeDto80
{
public const string TableName = Constants.DatabaseSchema.Tables.ContentType;
[Column("pk")]
[PrimaryKeyColumn(IdentitySeed = 535)]
public int PrimaryKey { get; set; }
[Column("nodeId")]
[ForeignKey(typeof(NodeDto))]
[Index(IndexTypes.UniqueNonClustered, Name = "IX_cmsContentType")]
public int NodeId { get; set; }
[Column("alias")]
[NullSetting(NullSetting = NullSettings.Null)]
public string Alias { get; set; }
[Column("icon")]
[Index(IndexTypes.NonClustered)]
[NullSetting(NullSetting = NullSettings.Null)]
public string Icon { get; set; }
[Column("thumbnail")]
[Constraint(Default = "folder.png")]
public string Thumbnail { get; set; }
[Column("description")]
[NullSetting(NullSetting = NullSettings.Null)]
[Length(1500)]
public string Description { get; set; }
[Column("isContainer")]
[Constraint(Default = "0")]
public bool IsContainer { get; set; }
[Column("allowAtRoot")]
[Constraint(Default = "0")]
public bool AllowAtRoot { get; set; }
[Column("variations")]
[Constraint(Default = "1" /*ContentVariation.InvariantNeutral*/)]
public byte Variations { get; set; }
[ResultColumn]
public NodeDto NodeDto { get; set; }
}
}
@@ -0,0 +1,142 @@
using NPoco;
using System;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0.Models
{
/// <summary>
/// Snapshot of the <see cref="PropertyDataDto"/> as it was at version 8.0
/// </summary>
/// <remarks>
/// This is required during migrations the schema of this table changed and running SQL against the new table would result in errors
/// </remarks>
[TableName(TableName)]
[PrimaryKey("id")]
[ExplicitColumns]
internal class PropertyDataDto80
{
public const string TableName = Constants.DatabaseSchema.Tables.PropertyData;
public const int VarcharLength = 512;
public const int SegmentLength = 256;
private decimal? _decimalValue;
// pk, not used at the moment (never updating)
[Column("id")]
[PrimaryKeyColumn]
public int Id { get; set; }
[Column("versionId")]
[ForeignKey(typeof(ContentVersionDto))]
[Index(IndexTypes.UniqueNonClustered, Name = "IX_" + TableName + "_VersionId", ForColumns = "versionId,propertyTypeId,languageId,segment")]
public int VersionId { get; set; }
[Column("propertyTypeId")]
[ForeignKey(typeof(PropertyTypeDto80))]
[Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_PropertyTypeId")]
public int PropertyTypeId { get; set; }
[Column("languageId")]
[ForeignKey(typeof(LanguageDto))]
[Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_LanguageId")]
[NullSetting(NullSetting = NullSettings.Null)]
public int? LanguageId { get; set; }
[Column("segment")]
[Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_Segment")]
[NullSetting(NullSetting = NullSettings.Null)]
[Length(SegmentLength)]
public string Segment { get; set; }
[Column("intValue")]
[NullSetting(NullSetting = NullSettings.Null)]
public int? IntegerValue { get; set; }
[Column("decimalValue")]
[NullSetting(NullSetting = NullSettings.Null)]
public decimal? DecimalValue
{
get => _decimalValue;
set => _decimalValue = value?.Normalize();
}
[Column("dateValue")]
[NullSetting(NullSetting = NullSettings.Null)]
public DateTime? DateValue { get; set; }
[Column("varcharValue")]
[NullSetting(NullSetting = NullSettings.Null)]
[Length(VarcharLength)]
public string VarcharValue { get; set; }
[Column("textValue")]
[NullSetting(NullSetting = NullSettings.Null)]
[SpecialDbType(SpecialDbTypes.NTEXT)]
public string TextValue { get; set; }
[ResultColumn]
[Reference(ReferenceType.OneToOne, ColumnName = "PropertyTypeId")]
public PropertyTypeDto80 PropertyTypeDto { get; set; }
[Ignore]
public object Value
{
get
{
if (IntegerValue.HasValue)
return IntegerValue.Value;
if (DecimalValue.HasValue)
return DecimalValue.Value;
if (DateValue.HasValue)
return DateValue.Value;
if (!string.IsNullOrEmpty(VarcharValue))
return VarcharValue;
if (!string.IsNullOrEmpty(TextValue))
return TextValue;
return null;
}
}
public PropertyDataDto80 Clone(int versionId)
{
return new PropertyDataDto80
{
VersionId = versionId,
PropertyTypeId = PropertyTypeId,
LanguageId = LanguageId,
Segment = Segment,
IntegerValue = IntegerValue,
DecimalValue = DecimalValue,
DateValue = DateValue,
VarcharValue = VarcharValue,
TextValue = TextValue,
PropertyTypeDto = PropertyTypeDto
};
}
protected bool Equals(PropertyDataDto other)
{
return Id == other.Id;
}
public override bool Equals(object other)
{
return
!ReferenceEquals(null, other) // other is not null
&& (ReferenceEquals(this, other) // and either ref-equals, or same id
|| other is PropertyDataDto pdata && pdata.Id == Id);
}
public override int GetHashCode()
{
// ReSharper disable once NonReadonlyMemberInGetHashCode
return Id;
}
}
}
@@ -0,0 +1,80 @@
using NPoco;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0.Models
{
/// <summary>
/// Snapshot of the <see cref="PropertyTypeDto"/> as it was at version 8.0
/// </summary>
/// <remarks>
/// This is required during migrations before 8.6 since the schema has changed and running SQL against the new table would result in errors
/// </remarks>
[TableName(Constants.DatabaseSchema.Tables.PropertyType)]
[PrimaryKey("id")]
[ExplicitColumns]
internal class PropertyTypeDto80
{
[Column("id")]
[PrimaryKeyColumn(IdentitySeed = 50)]
public int Id { get; set; }
[Column("dataTypeId")]
[ForeignKey(typeof(DataTypeDto), Column = "nodeId")]
public int DataTypeId { get; set; }
[Column("contentTypeId")]
[ForeignKey(typeof(ContentTypeDto), Column = "nodeId")]
public int ContentTypeId { get; set; }
[Column("propertyTypeGroupId")]
[NullSetting(NullSetting = NullSettings.Null)]
[ForeignKey(typeof(PropertyTypeGroupDto))]
public int? PropertyTypeGroupId { get; set; }
[Index(IndexTypes.NonClustered, Name = "IX_cmsPropertyTypeAlias")]
[Column("Alias")]
public string Alias { get; set; }
[Column("Name")]
[NullSetting(NullSetting = NullSettings.Null)]
public string Name { get; set; }
[Column("sortOrder")]
[Constraint(Default = "0")]
public int SortOrder { get; set; }
[Column("mandatory")]
[Constraint(Default = "0")]
public bool Mandatory { get; set; }
[Column("validationRegExp")]
[NullSetting(NullSetting = NullSettings.Null)]
public string ValidationRegExp { get; set; }
[Column("Description")]
[NullSetting(NullSetting = NullSettings.Null)]
[Length(2000)]
public string Description { get; set; }
[Column("variations")]
[Constraint(Default = "1" /*ContentVariation.InvariantNeutral*/)]
public byte Variations { get; set; }
[ResultColumn]
[Reference(ReferenceType.OneToOne, ColumnName = "DataTypeId")]
public DataTypeDto DataTypeDto { get; set; }
[Column("UniqueID")]
[NullSetting(NullSetting = NullSettings.NotNull)]
[Constraint(Default = SystemMethods.NewGuid)]
[Index(IndexTypes.UniqueNonClustered, Name = "IX_cmsPropertyTypeUniqueID")]
public Guid UniqueId { get; set; }
}
}
@@ -0,0 +1,33 @@
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Migrations.Upgrade.V_8_6_0
{
public class MissingDictionaryIndex : MigrationBase
{
public MissingDictionaryIndex(IMigrationContext context)
: base(context)
{
}
/// <summary>
/// Adds an index to the foreign key column <c>parent</c> on <c>DictionaryDto</c>'s table
/// if it doesn't already exist
/// </summary>
public override void Migrate()
{
var indexName = "IX_" + DictionaryDto.TableName + "_Parent";
if (!IndexExists(indexName))
{
Create
.Index(indexName)
.OnTable(DictionaryDto.TableName)
.OnColumn("parent")
.Ascending()
.WithOptions().NonClustered()
.Do();
}
}
}
}
+6 -6
View File
@@ -11,8 +11,8 @@ namespace Umbraco.Core
/// <remarks>An Udi can be fully qualified or "closed" eg umb://document/{guid} or "open" eg umb://document.</remarks>
[TypeConverter(typeof(UdiTypeConverter))]
public abstract class Udi : IComparable<Udi>
{
public Uri UriValue { get; }
{
public Uri UriValue { get; }
/// <summary>
/// Initializes a new instance of the Udi class.
@@ -35,7 +35,7 @@ namespace Umbraco.Core
UriValue = uriValue;
}
/// <summary>
/// Gets the entity type part of the identifier.
@@ -54,7 +54,7 @@ namespace Umbraco.Core
return UriValue.AbsoluteUri;
}
/// <summary>
/// Creates a root Udi for an entity type.
@@ -114,7 +114,7 @@ namespace Umbraco.Core
if (udiType == UdiType.GuidUdi)
return new GuidUdi(uri);
if (udiType == UdiType.GuidUdi)
if (udiType == UdiType.StringUdi)
return new StringUdi(uri);
throw new ArgumentException(string.Format("Uri \"{0}\" is not a valid udi.", uri));
@@ -166,6 +166,6 @@ namespace Umbraco.Core
return (udi1 == udi2) == false;
}
}
}
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.SqlSyntax;
@@ -84,10 +85,18 @@ namespace Umbraco.Core.Migrations
protected void ReplaceColumn<T>(string tableName, string currentName, string newName)
{
AddColumn<T>(tableName, newName, out var sqls);
Execute.Sql($"UPDATE {SqlSyntax.GetQuotedTableName(tableName)} SET {SqlSyntax.GetQuotedColumnName(newName)}={SqlSyntax.GetQuotedColumnName(currentName)}").Do();
foreach (var sql in sqls) Execute.Sql(sql).Do();
Delete.Column(currentName).FromTable(tableName).Do();
if (DatabaseType.IsSqlCe())
{
AddColumn<T>(tableName, newName, out var sqls);
Execute.Sql($"UPDATE {SqlSyntax.GetQuotedTableName(tableName)} SET {SqlSyntax.GetQuotedColumnName(newName)}={SqlSyntax.GetQuotedColumnName(currentName)}").Do();
foreach (var sql in sqls) Execute.Sql(sql).Do();
Delete.Column(currentName).FromTable(tableName).Do();
}
else
{
Execute.Sql(SqlSyntax.FormatColumnRename(tableName, currentName, newName)).Do();
AlterColumn<T>(tableName, newName);
}
}
protected bool TableExists(string tableName)
@@ -12,6 +12,7 @@ namespace Umbraco.Core.Migrations.Upgrade.Common
{
// remove those that may already have keys
Delete.KeysAndIndexes(Constants.DatabaseSchema.Tables.KeyValue).Do();
Delete.KeysAndIndexes(Constants.DatabaseSchema.Tables.PropertyData).Do();
// re-create *all* keys and indexes
foreach (var x in DatabaseSchemaCreator.OrderedTables)
@@ -193,6 +193,9 @@ namespace Umbraco.Core.Migrations.Upgrade
To<MissingContentVersionsIndexes>("{EE288A91-531B-4995-8179-1D62D9AA3E2E}");
To<AddMainDomLock>("{2AB29964-02A1-474D-BD6B-72148D2A53A2}");
To<MissingDictionaryIndex>("{a78e3369-8ea3-40ec-ad3f-5f76929d2b20}");
//FINAL
}
}
@@ -75,8 +75,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
var labelPropertyTypes = Database.Fetch<PropertyTypeDto>(Sql()
.Select<PropertyTypeDto>(x => x.Id, x => x.Alias)
.From<PropertyTypeDto>()
.Where<PropertyTypeDto>(x => x.DataTypeId == Constants.DataTypes.LabelString)
);
.Where<PropertyTypeDto>(x => x.DataTypeId == Constants.DataTypes.LabelString));
var intPropertyAliases = new[] { Constants.Conventions.Media.Width, Constants.Conventions.Media.Height, Constants.Conventions.Member.FailedPasswordAttempts };
var bigintPropertyAliases = new[] { Constants.Conventions.Media.Bytes };
@@ -101,16 +100,18 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
foreach (var value in values)
Database.Execute(Sql()
.Update<PropertyDataDto>(u => u
.Set(x => x.IntegerValue, string.IsNullOrWhiteSpace(value.VarcharValue) ? (int?) null : int.Parse(value.VarcharValue, NumberStyles.Any, CultureInfo.InvariantCulture))
.Set(x => x.TextValue, null))
.Set(x => x.IntegerValue, string.IsNullOrWhiteSpace(value.VarcharValue) ? (int?)null : int.Parse(value.VarcharValue, NumberStyles.Any, CultureInfo.InvariantCulture))
.Set(x => x.TextValue, null)
.Set(x => x.VarcharValue, null))
.Where<PropertyDataDto>(x => x.Id == value.Id));
values = Database.Fetch<PropertyDataValue>(Sql().Select<PropertyDataDto>(x => x.Id, x => x.VarcharValue).From<PropertyDataDto>().WhereIn<PropertyDataDto>(x => x.PropertyTypeId, dtPropertyTypes));
foreach (var value in values)
Database.Execute(Sql()
.Update<PropertyDataDto>(u => u
.Set(x => x.DateValue, string.IsNullOrWhiteSpace(value.VarcharValue) ? (DateTime?) null : DateTime.Parse(value.VarcharValue, CultureInfo.InvariantCulture, DateTimeStyles.None))
.Set(x => x.TextValue, null))
.Set(x => x.DateValue, string.IsNullOrWhiteSpace(value.VarcharValue) ? (DateTime?)null : DateTime.Parse(value.VarcharValue, CultureInfo.InvariantCulture, DateTimeStyles.None))
.Set(x => x.TextValue, null)
.Set(x => x.VarcharValue, null))
.Where<PropertyDataDto>(x => x.Id == value.Id));
// anything that's custom... ppl will have to figure it out manually, there isn't much we can do about it
@@ -4,8 +4,10 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NPoco;
using Umbraco.Core.Migrations.Upgrade.V_8_0_0.Models;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
@@ -47,14 +49,14 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
}
}
var propertyTypes = Database.Fetch<PropertyTypeDto>(Sql().Select<PropertyTypeDto>().From<PropertyTypeDto>());
var propertyTypes = Database.Fetch<PropertyTypeDto80>(Sql().Select<PropertyTypeDto80>().From<PropertyTypeDto80>());
foreach (var dto in propertyTypes)
{
dto.Variations = GetNewValue(dto.Variations);
Database.Update(dto);
}
var contentTypes = Database.Fetch<ContentTypeDto>(Sql().Select<ContentTypeDto>().From<ContentTypeDto>());
var contentTypes = Database.Fetch<ContentTypeDto80>(Sql().Select<ContentTypeDto80>().From<ContentTypeDto80>());
foreach (var dto in contentTypes)
{
dto.Variations = GetNewValue(dto.Variations);
@@ -62,57 +64,11 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
}
}
// we *need* to use this private DTO here, which does *not* have extra properties, which would kill the migration
// we *need* to use these private DTOs here, which does *not* have extra properties, which would kill the migration
[TableName(TableName)]
[PrimaryKey("pk")]
[ExplicitColumns]
private class ContentTypeDto
{
public const string TableName = Constants.DatabaseSchema.Tables.ContentType;
[Column("pk")]
[PrimaryKeyColumn(IdentitySeed = 535)]
public int PrimaryKey { get; set; }
[Column("nodeId")]
[ForeignKey(typeof(NodeDto))]
[Index(IndexTypes.UniqueNonClustered, Name = "IX_cmsContentType")]
public int NodeId { get; set; }
[Column("alias")]
[NullSetting(NullSetting = NullSettings.Null)]
public string Alias { get; set; }
[Column("icon")]
[Index(IndexTypes.NonClustered)]
[NullSetting(NullSetting = NullSettings.Null)]
public string Icon { get; set; }
[Column("thumbnail")]
[Constraint(Default = "folder.png")]
public string Thumbnail { get; set; }
[Column("description")]
[NullSetting(NullSetting = NullSettings.Null)]
[Length(1500)]
public string Description { get; set; }
[Column("isContainer")]
[Constraint(Default = "0")]
public bool IsContainer { get; set; }
[Column("allowAtRoot")]
[Constraint(Default = "0")]
public bool AllowAtRoot { get; set; }
[Column("variations")]
[Constraint(Default = "1" /*ContentVariation.InvariantNeutral*/)]
public byte Variations { get; set; }
[ResultColumn]
public NodeDto NodeDto { get; set; }
}
}
}
@@ -2,8 +2,7 @@
using System.Linq;
using System.Runtime.Serialization;
using Newtonsoft.Json;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Migrations.Upgrade.V_8_0_0.Models;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Dtos;
@@ -34,11 +33,11 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
}
var sqlPropertyTpes = Sql()
.Select<PropertyTypeDto>()
.From<PropertyTypeDto>()
.Where<PropertyTypeDto>(x => dataTypeIds.Contains(x.DataTypeId));
.Select<PropertyTypeDto80>()
.From<PropertyTypeDto80>()
.Where<PropertyTypeDto80>(x => dataTypeIds.Contains(x.DataTypeId));
var propertyTypeIds = Database.Fetch<PropertyTypeDto>(sqlPropertyTpes).Select(x => x.Id).ToList();
var propertyTypeIds = Database.Fetch<PropertyTypeDto80>(sqlPropertyTpes).Select(x => x.Id).ToList();
if (propertyTypeIds.Count == 0) return;
@@ -1,4 +1,5 @@
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
{
@@ -17,14 +18,24 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
AddColumn<MediaVersionDto>("id", out var sqls);
// SQLCE does not support UPDATE...FROM
var temp2 = Database.Fetch<dynamic>($@"SELECT v.versionId, v.id
if (Database.DatabaseType.IsSqlCe())
{
// SQLCE does not support UPDATE...FROM
var versions = Database.Fetch<dynamic>($@"SELECT v.versionId, v.id
FROM cmsContentVersion v
JOIN umbracoNode n on v.contentId=n.id
WHERE n.nodeObjectType='{Constants.ObjectTypes.Media}'");
foreach (var t in temp2)
Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.MediaVersion} SET id={t.id} WHERE versionId='{t.versionId}'").Do();
foreach (var t in versions)
Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.MediaVersion} SET id={t.id} WHERE versionId='{t.versionId}'").Do();
}
else
{
Database.Execute($@"UPDATE {Constants.DatabaseSchema.Tables.MediaVersion} SET id=v.id
FROM {Constants.DatabaseSchema.Tables.MediaVersion} m
JOIN cmsContentVersion v on m.versionId = v.versionId
JOIN umbracoNode n on v.contentId=n.id
WHERE n.nodeObjectType='{Constants.ObjectTypes.Media}'");
}
foreach (var sql in sqls)
Execute.Sql(sql).Do();
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Umbraco.Core.Migrations.Install;
@@ -19,6 +20,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
public override void Migrate()
{
MigratePropertyData();
CreatePropertyDataIndexes();
MigrateContentAndPropertyTypes();
MigrateContent();
MigrateVersions();
@@ -74,10 +76,20 @@ HAVING COUNT(v2.id) <> 1").Any())
{
Alter.Table(PreTables.PropertyData).AddColumn("versionId2").AsInt32().Nullable().Do();
// SQLCE does not support UPDATE...FROM
var temp = Database.Fetch<dynamic>($"SELECT id, versionId FROM {PreTables.ContentVersion}");
foreach (var t in temp)
Database.Execute($"UPDATE {PreTables.PropertyData} SET versionId2=@v2 WHERE versionId=@v1", new { v1 = t.versionId, v2 = t.id });
if (Database.DatabaseType.IsSqlCe())
{
// SQLCE does not support UPDATE...FROM
var versions = Database.Fetch<dynamic>($"SELECT id, versionId FROM {PreTables.ContentVersion}");
foreach (var t in versions)
Database.Execute($"UPDATE {PreTables.PropertyData} SET versionId2=@v2 WHERE versionId=@v1", new { v1 = t.versionId, v2 = t.id });
}
else
{
Database.Execute($@"UPDATE {PreTables.PropertyData} SET versionId2={PreTables.ContentVersion}.id
FROM {PreTables.ContentVersion}
INNER JOIN {PreTables.PropertyData} ON {PreTables.ContentVersion}.versionId = {PreTables.PropertyData}.versionId");
}
Delete.Column("versionId").FromTable(PreTables.PropertyData).Do();
ReplaceColumn<PropertyDataDto>(PreTables.PropertyData, "versionId2", "versionId");
}
@@ -90,6 +102,22 @@ HAVING COUNT(v2.id) <> 1").Any())
Rename.Table(PreTables.PropertyData).To(Constants.DatabaseSchema.Tables.PropertyData).Do();
}
private void CreatePropertyDataIndexes()
{
// Creates a temporary index on umbracoPropertyData to speed up other migrations which update property values.
// It will be removed in CreateKeysAndIndexes before the normal indexes for the table are created
var tableDefinition = Persistence.DatabaseModelDefinitions.DefinitionFactory.GetTableDefinition(typeof(PropertyDataDto), SqlSyntax);
Execute.Sql(SqlSyntax.FormatPrimaryKey(tableDefinition)).Do();
Create.Index("IX_umbracoPropertyData_Temp").OnTable(PropertyDataDto.TableName)
.WithOptions().Unique()
.WithOptions().NonClustered()
.OnColumn("versionId").Ascending()
.OnColumn("propertyTypeId").Ascending()
.OnColumn("languageId").Ascending()
.OnColumn("segment").Ascending()
.Do();
}
private void MigrateContentAndPropertyTypes()
{
if (!ColumnExists(PreTables.ContentType, "variations"))
@@ -153,22 +181,40 @@ HAVING COUNT(v2.id) <> 1").Any())
ReplaceColumn<ContentVersionDto>(PreTables.ContentVersion, "ContentId", "nodeId");
// populate contentVersion text, current and userId columns for documents
// SQLCE does not support UPDATE...FROM
var temp1 = Database.Fetch<dynamic>($"SELECT versionId, text, newest, documentUser FROM {PreTables.Document}");
foreach (var t in temp1)
Database.Execute($@"UPDATE {PreTables.ContentVersion} SET text=@text, {SqlSyntax.GetQuotedColumnName("current")}=@current, userId=@userId WHERE versionId=@versionId",
new { text = t.text, current = t.newest, userId=t.documentUser, versionId=t.versionId });
if (Database.DatabaseType.IsSqlCe())
{
// SQLCE does not support UPDATE...FROM
var documents = Database.Fetch<dynamic>($"SELECT versionId, text, published, newest, documentUser FROM {PreTables.Document}");
foreach (var t in documents)
Database.Execute($@"UPDATE {PreTables.ContentVersion} SET text=@text, {SqlSyntax.GetQuotedColumnName("current")}=@current, userId=@userId WHERE versionId=@versionId",
new { text = t.text, current = t.newest && !t.published, userId = t.documentUser, versionId = t.versionId });
}
else
{
Database.Execute($@"UPDATE {PreTables.ContentVersion} SET text=d.text, {SqlSyntax.GetQuotedColumnName("current")}=(d.newest & ~d.published), userId=d.documentUser
FROM {PreTables.ContentVersion} v INNER JOIN {PreTables.Document} d ON d.versionId = v.versionId");
}
// populate contentVersion text and current columns for non-documents, userId is default
// SQLCE does not support UPDATE...FROM
var temp2 = Database.Fetch<dynamic>($@"SELECT cver.versionId, n.text
if (Database.DatabaseType.IsSqlCe())
{
// SQLCE does not support UPDATE...FROM
var otherContent = Database.Fetch<dynamic>($@"SELECT cver.versionId, n.text
FROM {PreTables.ContentVersion} cver
JOIN {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.Node)} n ON cver.nodeId=n.id
WHERE cver.versionId NOT IN (SELECT versionId FROM {SqlSyntax.GetQuotedTableName(PreTables.Document)})");
foreach (var t in temp2)
Database.Execute($@"UPDATE {PreTables.ContentVersion} SET text=@text, {SqlSyntax.GetQuotedColumnName("current")}=1, userId=0 WHERE versionId=@versionId",
new { text = t.text, versionId=t.versionId });
foreach (var t in otherContent)
Database.Execute($@"UPDATE {PreTables.ContentVersion} SET text=@text, {SqlSyntax.GetQuotedColumnName("current")}=1, userId=0 WHERE versionId=@versionId",
new { text = t.text, versionId = t.versionId });
}
else
{
Database.Execute($@"UPDATE {PreTables.ContentVersion} SET text=n.text, {SqlSyntax.GetQuotedColumnName("current")}=1, userId=0
FROM {PreTables.ContentVersion} cver
JOIN {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.Node)} n ON cver.nodeId=n.id
WHERE cver.versionId NOT IN (SELECT versionId FROM {SqlSyntax.GetQuotedTableName(PreTables.Document)})");
}
// create table
Create.Table<DocumentVersionDto>(withoutKeysAndIndexes: true).Do();
@@ -179,36 +225,42 @@ SELECT cver.id, doc.templateId, doc.published
FROM {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} cver
JOIN {SqlSyntax.GetQuotedTableName(PreTables.Document)} doc ON doc.nodeId=cver.nodeId AND doc.versionId=cver.versionId");
// need to add extra rows for where published=newest
// 'cos INSERT above has inserted the 'published' document version
// and v8 always has a 'edited' document version too
var temp3 = Database.Fetch<dynamic>($@"SELECT doc.nodeId, doc.updateDate, doc.documentUser, doc.text, doc.templateId, cver.id versionId
Database.Execute($@"
INSERT INTO {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} (nodeId, versionId, versionDate, userId, {SqlSyntax.GetQuotedColumnName("current")}, text)
SELECT doc.nodeId, NEWID(), doc.updateDate, doc.documentUser, 1, doc.text
FROM {SqlSyntax.GetQuotedTableName(PreTables.Document)} doc
JOIN {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} cver ON doc.nodeId=cver.nodeId AND doc.versionId=cver.versionId
WHERE doc.newest=1 AND doc.published=1");
var getIdentity = "@@@@IDENTITY";
foreach (var t in temp3)
{
Database.Execute($@"INSERT INTO {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} (nodeId, versionId, versionDate, userId, {SqlSyntax.GetQuotedColumnName("current")}, text)
VALUES (@nodeId, @versionId, @versionDate, @userId, 1, @text)", new { nodeId=t.nodeId, versionId=Guid.NewGuid(), versionDate=t.updateDate, userId=t.documentUser, text=t.text });
var id = Database.ExecuteScalar<int>("SELECT " + getIdentity);
Database.Execute($"UPDATE {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} SET {SqlSyntax.GetQuotedColumnName("current")}=0 WHERE nodeId=@0 AND id<>@1", (int) t.nodeId, id);
Database.Execute($@"INSERT INTO {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.DocumentVersion)} (id, templateId, published)
VALUES (@id, @templateId, 0)", new { id=id, templateId=t.templateId });
var versionId = (int) t.versionId;
var pdatas = Database.Fetch<PropertyDataDto>(Sql().Select<PropertyDataDto>().From<PropertyDataDto>().Where<PropertyDataDto>(x => x.VersionId == versionId));
foreach (var pdata in pdatas)
{
pdata.VersionId = id;
Database.Insert(pdata);
}
}
Database.Execute($@"
INSERT INTO {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.DocumentVersion)} (id, templateId, published)
SELECT cverNew.id, doc.templateId, 0
FROM {SqlSyntax.GetQuotedTableName(PreTables.Document)} doc
JOIN {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} cverNew ON doc.nodeId = cverNew.nodeId
WHERE doc.newest=1 AND doc.published=1 AND cverNew.{SqlSyntax.GetQuotedColumnName("current")} = 1");
Database.Execute($@"
INSERT INTO {SqlSyntax.GetQuotedTableName(PropertyDataDto.TableName)} (propertytypeid,languageId,segment,textValue,varcharValue,decimalValue,intValue,dateValue,versionId)
SELECT propertytypeid,languageId,segment,textValue,varcharValue,decimalValue,intValue,dateValue,cverNew.id
FROM {SqlSyntax.GetQuotedTableName(PreTables.Document)} doc
JOIN {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} cver ON doc.nodeId=cver.nodeId AND doc.versionId=cver.versionId
JOIN {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} cverNew ON doc.nodeId = cverNew.nodeId
JOIN {SqlSyntax.GetQuotedTableName(PropertyDataDto.TableName)} pd ON pd.versionId=cver.id
WHERE doc.newest=1 AND doc.published=1 AND cverNew.{SqlSyntax.GetQuotedColumnName("current")} = 1");
// reduce document to 1 row per content
Database.Execute($@"DELETE FROM {PreTables.Document}
WHERE versionId NOT IN (SELECT (versionId) FROM {PreTables.ContentVersion} WHERE {SqlSyntax.GetQuotedColumnName("current")} = 1) AND (published<>1 OR newest<>1)");
// ensure that documents with a published version are marked as published
Database.Execute($@"UPDATE {PreTables.Document} SET published=1 WHERE nodeId IN (
SELECT nodeId FROM {PreTables.ContentVersion} cv INNER JOIN {Constants.DatabaseSchema.Tables.DocumentVersion} dv ON dv.id = cv.id WHERE dv.published=1)");
// drop some document columns
Delete.Column("text").FromTable(PreTables.Document).Do();
Delete.Column("templateId").FromTable(PreTables.Document).Do();
@@ -223,7 +275,7 @@ WHERE versionId NOT IN (SELECT (versionId) FROM {PreTables.ContentVersion} WHERE
if (!ColumnExists(PreTables.Document, "edited"))
{
AddColumn<DocumentDto>(PreTables.Document, "edited", out var sqls);
Database.Execute($"UPDATE {SqlSyntax.GetQuotedTableName(PreTables.Document)} SET edited=0");
Database.Execute($"UPDATE {SqlSyntax.GetQuotedTableName(PreTables.Document)} SET edited=~published");
foreach (var sql in sqls) Database.Execute(sql);
}
@@ -240,11 +292,15 @@ JOIN {Constants.DatabaseSchema.Tables.PropertyData} v1 ON cv1.id=v1.versionId
JOIN {PreTables.ContentVersion} cv2 ON n.id=cv2.nodeId
JOIN {Constants.DatabaseSchema.Tables.DocumentVersion} dv ON cv2.id=dv.id AND dv.published=1
JOIN {Constants.DatabaseSchema.Tables.PropertyData} v2 ON cv2.id=v2.versionId
WHERE v1.propertyTypeId=v2.propertyTypeId AND v1.languageId=v2.languageId AND v1.segment=v2.segment");
WHERE v1.propertyTypeId=v2.propertyTypeId
AND (v1.languageId=v2.languageId OR (v1.languageId IS NULL AND v2.languageId IS NULL))
AND (v1.segment=v2.segment OR (v1.segment IS NULL AND v2.segment IS NULL))");
var updatedIds = new HashSet<int>();
foreach (var t in temp)
if (t.intValue1 != t.intValue2 || t.decimalValue1 != t.decimalValue2 || t.dateValue1 != t.dateValue2 || t.varcharValue1 != t.varcharValue2 || t.textValue1 != t.textValue2)
Database.Execute("UPDATE {SqlSyntax.GetQuotedTableName(PreTables.Document)} SET edited=1 WHERE nodeId=@nodeIdd", new { t.id });
if (updatedIds.Add((int)t.id))
Database.Execute($"UPDATE {SqlSyntax.GetQuotedTableName(PreTables.Document)} SET edited=1 WHERE nodeId=@nodeId", new { nodeId = t.id });
// drop more columns
Delete.Column("versionId").FromTable(PreTables.ContentVersion).Do();
@@ -5,6 +5,7 @@ using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Umbraco.Core.Migrations.PostMigrations;
using Umbraco.Core.Migrations.Upgrade.V_8_0_0.Models;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.Services;
@@ -27,15 +28,15 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_1_0
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
var sqlPropertyData = Sql()
.Select<PropertyDataDto>(r => r.Select(x => x.PropertyTypeDto, r1 => r1.Select(x => x.DataTypeDto)))
.From<PropertyDataDto>()
.InnerJoin<PropertyTypeDto>().On<PropertyDataDto, PropertyTypeDto>((left, right) => left.PropertyTypeId == right.Id)
.InnerJoin<DataTypeDto>().On<PropertyTypeDto, DataTypeDto>((left, right) => left.DataTypeId == right.NodeId)
.Select<PropertyDataDto80>(r => r.Select(x => x.PropertyTypeDto, r1 => r1.Select(x => x.DataTypeDto)))
.From<PropertyDataDto80>()
.InnerJoin<PropertyTypeDto80>().On<PropertyDataDto80, PropertyTypeDto80>((left, right) => left.PropertyTypeId == right.Id)
.InnerJoin<DataTypeDto>().On<PropertyTypeDto80, DataTypeDto>((left, right) => left.DataTypeId == right.NodeId)
.Where<DataTypeDto>(x =>
x.EditorAlias == Constants.PropertyEditors.Aliases.TinyMce ||
x.EditorAlias == Constants.PropertyEditors.Aliases.Grid);
var properties = Database.Fetch<PropertyDataDto>(sqlPropertyData);
var properties = Database.Fetch<PropertyDataDto80>(sqlPropertyData);
var exceptions = new List<Exception>();
foreach (var property in properties)
@@ -43,6 +44,8 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_1_0
var value = property.TextValue;
if (string.IsNullOrWhiteSpace(value)) continue;
bool propertyChanged = false;
if (property.PropertyTypeDto.DataTypeDto.EditorAlias == Constants.PropertyEditors.Aliases.Grid)
{
try
@@ -55,7 +58,8 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_1_0
var controlValue = control["value"];
if (controlValue?.Type == JTokenType.String)
{
control["value"] = UpdateMediaUrls(mediaLinkPattern, controlValue.Value<string>());
control["value"] = UpdateMediaUrls(mediaLinkPattern, controlValue.Value<string>(), out var controlChanged);
propertyChanged |= controlChanged;
}
}
@@ -76,10 +80,11 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_1_0
}
else
{
property.TextValue = UpdateMediaUrls(mediaLinkPattern, value);
property.TextValue = UpdateMediaUrls(mediaLinkPattern, value, out propertyChanged);
}
Database.Update(property);
if (propertyChanged)
Database.Update(property);
}
@@ -91,10 +96,14 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_1_0
Context.AddPostMigration<RebuildPublishedSnapshot>();
}
private string UpdateMediaUrls(Regex mediaLinkPattern, string value)
private string UpdateMediaUrls(Regex mediaLinkPattern, string value, out bool changed)
{
return mediaLinkPattern.Replace(value, match =>
bool matched = false;
var result = mediaLinkPattern.Replace(value, match =>
{
matched = true;
// match groups:
// - 1 = from the beginning of the a tag until href attribute value begins
// - 2 = the href attribute value excluding the querystring (if present)
@@ -106,6 +115,10 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_1_0
? match.Value
: $"{match.Groups[1].Value}/{{localLink:{media.GetUdi()}}}{match.Groups[3].Value}";
});
changed = matched;
return result;
}
}
}
@@ -4,21 +4,30 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_6_0
{
public class MissingContentVersionsIndexes : MigrationBase
{
private const string IndexName = "IX_" + ContentVersionDto.TableName + "_NodeId";
public MissingContentVersionsIndexes(IMigrationContext context) : base(context)
{
}
public override void Migrate()
{
Create
.Index("IX_" + ContentVersionDto.TableName + "_NodeId")
.OnTable(ContentVersionDto.TableName)
.OnColumn("nodeId")
.Ascending()
.OnColumn("current")
.Ascending()
.WithOptions().NonClustered()
.Do();
// We must check before we create an index because if we are upgrading from v7 we force re-create all
// indexes in the whole DB and then this would throw
if (!IndexExists(IndexName))
{
Create
.Index(IndexName)
.OnTable(ContentVersionDto.TableName)
.OnColumn("nodeId")
.Ascending()
.OnColumn("current")
.Ascending()
.WithOptions().NonClustered()
.Do();
}
}
}
}
@@ -5,11 +5,12 @@ using Umbraco.Core.Persistence.DatabaseAnnotations;
namespace Umbraco.Core.Persistence.Dtos
{
[TableName(Constants.DatabaseSchema.Tables.DictionaryEntry)]
[TableName(TableName)]
[PrimaryKey("pk")]
[ExplicitColumns]
internal class DictionaryDto
{
public const string TableName = Constants.DatabaseSchema.Tables.DictionaryEntry;
[Column("pk")]
[PrimaryKeyColumn]
public int PrimaryKey { get; set; }
@@ -21,6 +22,7 @@ namespace Umbraco.Core.Persistence.Dtos
[Column("parent")]
[NullSetting(NullSetting = NullSettings.Null)]
[ForeignKey(typeof(DictionaryDto), Column = "id")]
[Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_Parent")]
public Guid? Parent { get; set; }
[Column("key")]
+3 -3
View File
@@ -15221,9 +15221,9 @@
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
},
"tinymce": {
"version": "4.9.2",
"resolved": "https://registry.npmjs.org/tinymce/-/tinymce-4.9.2.tgz",
"integrity": "sha512-ZRoTGG4GAsOI73QPSNkabO7nkoYw9H6cglRB44W2mMkxSiqxYi8WJlgkUphk0fDqo6ZD6r3E+NSP4UHxF2lySg=="
"version": "4.9.7",
"resolved": "https://registry.npmjs.org/tinymce/-/tinymce-4.9.7.tgz",
"integrity": "sha512-cj0HvUuniTuIjOAJdRt5BUfeQqM5yHjbA2NOub9HUHXlCrT9OwD9WBPU6tGlaPC2l2I4eGoOnT8llosZRdQU5Q=="
},
"tmp": {
"version": "0.0.33",
+1 -1
View File
@@ -42,7 +42,7 @@
"npm": "6.13.6",
"signalr": "2.4.0",
"spectrum-colorpicker": "1.8.0",
"tinymce": "4.9.2",
"tinymce": "4.9.7",
"typeahead.js": "0.11.1",
"underscore": "1.9.1"
},
@@ -130,6 +130,7 @@
view: "default",
content: labels.doctypeChangeWarning,
submitButtonLabelKey: "general_continue",
submitButtonStyle: "warning",
closeButtonLabelKey: "general_cancel",
submit: function () {
openDocTypeEditor(documentType);
@@ -58,9 +58,9 @@
/** Called when the component has linked all elements, this is when the form controller is available */
function postLink() {
//set the content to dirty if the header changes
unsubscribe.push($scope.$watch("contentHeaderForm.$dirty",
function(newValue, oldValue) {
if (newValue === true) {
unsubscribe.push($scope.$watch("vm.editor.content.name",
function (newValue, oldValue) {
if (newValue !== oldValue) {
vm.editor.content.isDirty = true;
}
}));
@@ -14,7 +14,8 @@ function treeSearchBox(localizationService, searchService, $q) {
section: "@",
datatypeKey: "@",
hideSearchCallback: "=",
searchCallback: "="
searchCallback: "=",
autoFocus: "="
},
restrict: "E", // restrict to an element
replace: true, // replace the html element with the template
@@ -308,6 +308,8 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
var src = imgUrl + "?width=" + newSize.width + "&height=" + newSize.height;
editor.dom.setAttrib(imageDomElement, 'data-mce-src', src);
}
editor.execCommand("mceAutoResize", false, null, null);
}
}
@@ -672,7 +672,8 @@
}
.imagecropper .umb-sortable-thumbnails li .crop-name,
.imagecropper .umb-sortable-thumbnails li .crop-size {
.imagecropper .umb-sortable-thumbnails li .crop-size,
.imagecropper .umb-sortable-thumbnails li .crop-annotation {
display: block;
text-align: left;
font-size: 13px;
@@ -684,17 +685,28 @@
margin: 10px 0 5px;
}
.imagecropper .umb-sortable-thumbnails li .crop-size {
.imagecropper .umb-sortable-thumbnails li .crop-size,
.imagecropper .umb-sortable-thumbnails li .crop-annotation {
font-size: 10px;
font-style: italic;
margin: 0 0 5px;
}
.imagecropper .umb-sortable-thumbnails li .crop-annotation {
color: @gray-6;
}
.btn-crop-delete {
display: block;
text-align: left;
}
.imagecropper .cropList-container {
h5 {
margin-left: 10px;
margin-top: 0;
}
}
//
// folder-browser
// --------------------------------------------------
@@ -16,7 +16,7 @@ function IconPickerController($scope, iconHelper, localizationService) {
vm.close = close;
vm.colors = [
{ name: "Black", value: "color-black" },
{ name: "Black", value: "color-black", default: true },
{ name: "Blue Grey", value: "color-blue-grey" },
{ name: "Grey", value: "color-grey" },
{ name: "Brown", value: "color-brown" },
@@ -49,7 +49,7 @@ function IconPickerController($scope, iconHelper, localizationService) {
});
// set a default color if nothing is passed in
vm.color = $scope.model.color ? findColor($scope.model.color) : vm.colors[0];
vm.color = $scope.model.color ? findColor($scope.model.color) : vm.colors.find(x => x.default);
// if an icon is passed in - preselect it
vm.icon = $scope.model.icon ? $scope.model.icon : undefined;
@@ -71,12 +71,13 @@ function IconPickerController($scope, iconHelper, localizationService) {
}
function findColor(value) {
return _.findWhere(vm.colors, {value: value});
return vm.colors.find(x => x.value === value);
}
function selectColor(color, $index, $event) {
$scope.model.color = color.value;
vm.color = color;
function selectColor(color) {
let newColor = (color || vm.colors.find(x => x.default));
$scope.model.color = newColor.value;
vm.color = newColor;
}
function close() {
@@ -1,7 +1,7 @@
//used for the media picker dialog
angular.module("umbraco")
.controller("Umbraco.Editors.MediaPickerController",
function ($scope, $timeout, mediaResource, entityResource, userService, mediaHelper, mediaTypeHelper, eventsService, treeService, localStorageService, localizationService) {
function ($scope, mediaResource, entityResource, userService, mediaHelper, mediaTypeHelper, eventsService, treeService, localStorageService, localizationService, editorService, umbSessionStorage) {
var vm = this;
@@ -37,6 +37,11 @@ angular.module("umbraco")
$scope.cropSize = dialogOptions.cropSize;
$scope.lastOpenedNode = localStorageService.get("umbLastOpenedMediaNodeId");
$scope.lockedFolder = true;
$scope.allowMediaEdit = dialogOptions.allowMediaEdit ? dialogOptions.allowMediaEdit : false;
$scope.filterOptions = {
excludeSubFolders: umbSessionStorage.get("mediaPickerExcludeSubFolders") || false
};
var userStartNodes = [];
@@ -392,6 +397,7 @@ angular.module("umbraco")
}
function toggle() {
umbSessionStorage.set("mediaPickerExcludeSubFolders", $scope.filterOptions.excludeSubFolders);
// Make sure to activate the changeSearch function everytime the toggle is clicked
changeSearch();
}
@@ -404,7 +410,7 @@ angular.module("umbraco")
function searchMedia() {
vm.loading = true;
entityResource.getPagedDescendants($scope.currentFolder.id, "Media", vm.searchOptions)
entityResource.getPagedDescendants($scope.filterOptions.excludeSubFolders ? $scope.currentFolder.id : $scope.startNodeId, "Media", vm.searchOptions)
.then(function (data) {
// update image data to work with image grid
angular.forEach(data.items, function (mediaItem) {
@@ -32,10 +32,10 @@
<div class="form-search__toggle">
<umb-checkbox
model="showChilds"
model="filterOptions.excludeSubFolders"
on-change="vm.toggle()"
text="Include subfolders in search"
label-key="general_includeFromsubFolders">
label-key="general_excludeFromSubFolders">
</umb-checkbox>
</div>
</div>
@@ -112,9 +112,8 @@
disable-folder-select={{disableFolderSelect}}
only-images={{onlyImages}}
only-folders={{onlyFolders}}
include-sub-folders={{showChilds}}
current-folder-id="{{currentFolder.id}}"
allow-open-folder="!disableFolderSelect">
include-sub-folders={{!filterOptions.excludeSubFolders}}
current-folder-id="{{currentFolder.id}}">
</umb-media-grid>
@@ -64,6 +64,8 @@ angular.module("umbraco").controller("Umbraco.Editors.TreePickerController",
var currentNode = $scope.model.currentNode;
var previouslyFocusedElement = null;
function initDialogTree() {
vm.dialogTreeApi.callbacks.treeLoaded(treeLoadedHandler);
// TODO: Also deal with unexpanding!!
@@ -487,6 +489,7 @@ angular.module("umbraco").controller("Umbraco.Editors.TreePickerController",
}
function openMiniListView(node) {
previouslyFocusedElement = document.activeElement;
vm.miniListView = node;
}
@@ -650,6 +653,12 @@ angular.module("umbraco").controller("Umbraco.Editors.TreePickerController",
function closeMiniListView() {
vm.miniListView = undefined;
if (previouslyFocusedElement) {
$timeout(function () {
previouslyFocusedElement.focus();
previouslyFocusedElement = null;
});
}
}
function listViewItemsLoaded(items) {
@@ -37,7 +37,8 @@
search-from-name="{{vm.searchInfo.searchFromName}}"
show-search="{{vm.searchInfo.showSearch}}"
datatype-key="{{vm.searchInfo.dataTypeKey}}"
section="{{vm.section}}">
section="{{vm.section}}"
auto-focus="true">
</umb-tree-search-box>
</div>
@@ -5,9 +5,9 @@
ng-model="term"
class="umb-search-field search-query -full-width-input"
placeholder="{{searchPlaceholderText}}"
focus-when="{{showSearch}}">
umb-auto-focus="{{autoFocus ? 'true' : 'false'}}">
<h4 ng-if="showSearch && searchFromName">
<small><localize key="general_search">Search</localize>:&nbsp;</small>
{{searchFromName}}
</h4>
</div>
</div>
@@ -43,6 +43,7 @@
ng-model="search"
ng-change="searchMiniListView(search, miniListView)"
prevent-enter-submit
umb-auto-focus
no-dirty-check>
</div>
</form>
@@ -6,8 +6,8 @@
<umb-load-indicator ng-if="vm.loading"></umb-load-indicator>
<div class="umb-pane" ng-show="!vm.loading">
<div class="umb-pane" ng-show="!vm.loading" role="dialog" aria-labelledby="content_rights_title" aria-describedby="content_rights_description">
<form name="permissionsForm">
<div ng-show="vm.saveError">
@@ -23,8 +23,8 @@
</div>
</div>
<h5><localize key="defaultdialogs_permissionsSet">Set permissions for</localize> {{ currentNode.name }}</h5>
<p class="abstract" style="margin-bottom: 20px;"><localize key="defaultdialogs_permissionsHelp"></localize></p>
<h5 id="content_rights_title"><localize key="defaultdialogs_permissionsSet">Set permissions for</localize> {{ currentNode.name }}</h5>
<p id="content_rights_description" class="abstract" style="margin-bottom: 20px;"><localize key="defaultdialogs_permissionsHelp"></localize></p>
<div style="position: relative; display: inline-block; margin-bottom: 20px;">
<umb-button
@@ -13,6 +13,7 @@ angular.module('umbraco')
$scope.clear = clear;
$scope.reset = reset;
$scope.close = close;
$scope.isCustomCrop = isCustomCrop;
$scope.focalPointChanged = focalPointChanged;
//declare a special method which will be called whenever the value has changed from the server
$scope.model.onValueChanged = onValueChanged;
@@ -201,6 +202,10 @@ angular.module('umbraco')
$scope.imageCropperForm.$setDirty();
};
function isCustomCrop(crop) {
return !!crop.coordinates;
}
var unsubscribe = $scope.$on("formSubmitting", function () {
$scope.currentCrop = null;
$scope.currentPoint = null;
@@ -60,6 +60,7 @@
<div class="crop-information">
<span class="crop-name crop-text">{{value.alias}}</span>
<span class="crop-size crop-text">{{value.width}}px x {{value.height}}px</span>
<span class="crop-annotation crop-text"><localize key="imagecropper_customCrop" ng-show="isCustomCrop(value)">User defined</localize>&nbsp;</span>
</div>
</li>
</ul>
@@ -661,7 +661,7 @@
<key alias="icon">Ikon</key>
<key alias="id">Id</key>
<key alias="import">Importer</key>
<key alias="includeFromsubFolders">Inkludér undermapper i søgning</key>
<key alias="excludeFromSubFolders">Søg kun i denne mappe</key>
<key alias="info">Info</key>
<key alias="innerMargin">Indre margen</key>
<key alias="insert">Indsæt</key>
@@ -1086,6 +1086,7 @@ Mange hilsner fra Umbraco robotten
<key alias="addCrop">Tilføj ny beskæring</key>
<key alias="updateEditCrop">Acceptér</key>
<key alias="undoEditCrop">Fortryd</key>
<key alias="customCrop">Brugerdefineret</key>
</area>
<area alias="rollback">
<key alias="headline">Vælg en version at sammenligne med den nuværende version</key>
@@ -674,7 +674,7 @@
<key alias="icon">Icon</key>
<key alias="id">Id</key>
<key alias="import">Import</key>
<key alias="includeFromsubFolders">Include subfolders in search</key>
<key alias="excludeFromSubFolders">Search only this folder</key>
<key alias="info">Info</key>
<key alias="innerMargin">Inner margin</key>
<key alias="insert">Insert</key>
@@ -1318,6 +1318,7 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="addCrop">Add new crop</key>
<key alias="updateEditCrop">Done</key>
<key alias="undoEditCrop">Undo edits</key>
<key alias="customCrop">User defined</key>
</area>
<area alias="rollback">
<key alias="headline">Select a version to compare with the current version</key>
@@ -676,7 +676,7 @@
<key alias="icon">Icon</key>
<key alias="id">Id</key>
<key alias="import">Import</key>
<key alias="includeFromsubFolders">Include subfolders in search</key>
<key alias="excludeFromSubFolders">Search only this folder</key>
<key alias="info">Info</key>
<key alias="innerMargin">Inner margin</key>
<key alias="insert">Insert</key>
@@ -1316,11 +1316,12 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="externalLinkPlaceholder">Enter the link</key>
</area>
<area alias="imagecropper">
<key alias="reset">Reset crop</key>
<key alias="reset">Reset crop</key>
<key alias="saveCrop">Save crop</key>
<key alias="addCrop">Add new crop</key>
<key alias="updateEditCrop">Done</key>
<key alias="undoEditCrop">Undo edits</key>
<key alias="updateEditCrop">Done</key>
<key alias="undoEditCrop">Undo edits</key>
<key alias="customCrop">User defined</key>
</area>
<area alias="rollback">
<key alias="headline">Select a version to compare with the current version</key>
@@ -241,6 +241,14 @@
By default you can call any content Id in the url and show the content with that id, for example:
http://mysite.com/1092 or http://mysite.com/1092.aspx would render the content with id 1092. Setting
this setting to true stops that behavior
@disableRedirectUrlTracking
When the URL changes for content, redirects are automatically created for redirect handling within the
request pipeline. Setting this setting to true stops the automatic creation of redirects. Note that this
does not stop the request pipeline from handling any previously created redirects.
@urlProviderMode
By default Umbraco automatically figures out if internal URLs should be rendered as relative or absolute,
depending on the current request and the configured domains. By setting this setting to "Relative" or
"Absolute" you can force Umbraco to always render URLs as either relative or absolute.
@umbracoApplicationUrl
The url of the Umbraco application. By default, Umbraco will figure it out from the first request.
Configure it here if you need anything specific. Needs to be a complete url with scheme and umbraco
@@ -197,7 +197,9 @@ namespace Umbraco.Web.Editors
throw new HttpResponseException(HttpStatusCode.NotFound);
}
var template = Services.FileService.CreateTemplateWithIdentity(display.Name, display.Alias, display.Content, master);
// we need to pass the template name as alias to keep the template file casing consistent with templates created with content
// - see comment in FileService.CreateTemplateForContentType for additional details
var template = Services.FileService.CreateTemplateWithIdentity(display.Name, display.Name, display.Content, master);
Mapper.Map(template, display);
}
-20
View File
@@ -1,20 +0,0 @@
using System.Web.UI;
namespace Umbraco.Web
{
/// <summary>
/// A formless page for use with the rendering a control in a page via Server.Execute.
/// This ignores the check to check for a form control on the page.
/// </summary>
/// <remarks>
/// UmbracoHelper currently uses this for rendering macros but could be used anywhere we want when rendering
/// a page with Server.Execute.
/// SD: I have a custom MVC engine that uses this in my own internal libs if we want to pull it out which is called ViewManager
/// and works really well for things like this.
/// </remarks>
internal class FormlessPage : Page
{
public override void VerifyRenderingInServerForm(Control control) { }
}
}
@@ -86,7 +86,7 @@ namespace Umbraco.Web.Models.Mapping
target.Icon = source.ContentType.Icon;
target.Id = source.Id;
target.IsBlueprint = source.Blueprint;
target.IsChildOfListView = DermineIsChildOfListView(source);
target.IsChildOfListView = DetermineIsChildOfListView(source);
target.IsContainer = source.ContentType.IsContainer;
target.IsElement = source.ContentType.IsElement;
target.Key = source.Key;
@@ -217,7 +217,7 @@ namespace Umbraco.Web.Models.Mapping
return source.CultureInfos.TryGetValue(culture, out var name) && !name.Name.IsNullOrWhiteSpace() ? name.Name : $"({source.Name})";
}
private bool DermineIsChildOfListView(IContent source)
private bool DetermineIsChildOfListView(IContent source)
{
// map the IsChildOfListView (this is actually if it is a descendant of a list view!)
var parent = _contentService.GetParent(source);
@@ -62,7 +62,7 @@ namespace Umbraco.Web.Models.Mapping
target.CreateDate = source.CreateDate;
target.Icon = source.ContentType.Icon;
target.Id = source.Id;
target.IsChildOfListView = DermineIsChildOfListView(source);
target.IsChildOfListView = DetermineIsChildOfListView(source);
target.Key = source.Key;
target.MediaLink = string.Join(",", source.GetUrls(_umbracoSettingsSection.Content, _logger, _propertyEditorCollection));
target.Name = source.Name;
@@ -101,7 +101,7 @@ namespace Umbraco.Web.Models.Mapping
target.VariesByCulture = source.ContentType.VariesByCulture();
}
private bool DermineIsChildOfListView(IMedia source)
private bool DetermineIsChildOfListView(IMedia source)
{
// map the IsChildOfListView (this is actually if it is a descendant of a list view!)
var parent = _mediaService.GetParent(source);
@@ -114,6 +114,19 @@ namespace Umbraco.Web.Security
return SignInStatus.LockedOut;
}
// We need to verify that the user belongs to one or more groups that define content and media start nodes.
// To do so we have to create the user claims identity and validate the calculated start nodes.
var userIdentity = await CreateUserIdentityAsync(user);
if (userIdentity is UmbracoBackOfficeIdentity backOfficeIdentity)
{
if (backOfficeIdentity.StartContentNodes.Length == 0 || backOfficeIdentity.StartMediaNodes.Length == 0)
{
_logger.WriteCore(TraceEventType.Information, 0,
$"Login attempt failed for username {userName} from IP address {_request.RemoteIpAddress}, no content and/or media start nodes could be found for any of the user's groups", null, null);
return SignInStatus.Failure;
}
}
await UserManager.ResetAccessFailedCountAsync(user.Id);
return await SignInOrTwoFactor(user, isPersistent);
}
@@ -13,14 +13,10 @@ namespace Umbraco.Web.Templates
this._getMediaUrl = getMediaUrl;
}
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
public HtmlImageSourceParser(IUmbracoContextAccessor umbracoContextAccessor)
{
if (umbracoContextAccessor?.UmbracoContext?.UrlProvider == null)
{
return;
}
_getMediaUrl = (guid) => umbracoContextAccessor.UmbracoContext.UrlProvider.GetMediaUrl(guid);
_umbracoContextAccessor = umbracoContextAccessor;
}
private static readonly Regex ResolveImgPattern = new Regex(@"(<img[^>]*src="")([^""\?]*)((?:\?[^""]*)?""[^>]*data-udi="")([^""]*)(""[^>]*>)",
@@ -29,7 +25,7 @@ namespace Umbraco.Web.Templates
private static readonly Regex DataUdiAttributeRegex = new Regex(@"data-udi=\\?(?:""|')(?<udi>umb://[A-z0-9\-]+/[A-z0-9]+)\\?(?:""|')",
RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
private readonly Func<Guid, string> _getMediaUrl;
private Func<Guid, string> _getMediaUrl;
/// <summary>
/// Parses out media UDIs from an html string based on 'data-udi' html attributes
@@ -57,12 +53,8 @@ namespace Umbraco.Web.Templates
/// <remarks>Umbraco image tags are identified by their data-udi attributes</remarks>
public string EnsureImageSources(string text)
{
// no point in doing any processing if we don't have
// a function to retrieve Urls
if (_getMediaUrl == null)
{
return text;
}
if(_getMediaUrl == null)
_getMediaUrl = (guid) => _umbracoContextAccessor.UmbracoContext.UrlProvider.GetMediaUrl(guid);
return ResolveImgPattern.Replace(text, match =>
{
-3
View File
@@ -644,9 +644,6 @@
<Compile Include="Mvc\UmbracoViewPageOfTModel.cs" />
<Compile Include="PublishedContentExtensions.cs" />
<Compile Include="ExamineExtensions.cs" />
<Compile Include="FormlessPage.cs">
<SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="HtmlHelperRenderExtensions.cs" />
<Compile Include="Scheduling\SchedulerComponent.cs" />
<Compile Include="ModelStateExtensions.cs" />