Port v7@2aa0dfb2c5 - WIP

This commit is contained in:
Stephan
2018-03-21 14:40:59 +01:00
parent 41948607d0
commit 8bfb8e2b72
46 changed files with 1240 additions and 159 deletions
@@ -153,11 +153,13 @@ namespace Umbraco.Core.Migrations.Install
_database.Insert(Constants.DatabaseSchema.Tables.UserGroup, "id", false, new UserGroupDto { Id = 2, StartMediaId = -1, StartContentId = -1, Alias = "writer", Name = "Writers", DefaultPermissions = "CAH:F", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-edit" });
_database.Insert(Constants.DatabaseSchema.Tables.UserGroup, "id", false, new UserGroupDto { Id = 3, StartMediaId = -1, StartContentId = -1, Alias = "editor", Name = "Editors", DefaultPermissions = "CADMOSKTPUZ:5Fï", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-tools" });
_database.Insert(Constants.DatabaseSchema.Tables.UserGroup, "id", false, new UserGroupDto { Id = 4, StartMediaId = -1, StartContentId = -1, Alias = Constants.Security.TranslatorGroupAlias, Name = "Translators", DefaultPermissions = "AF", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-globe" });
_database.Insert(Constants.DatabaseSchema.Tables.UserGroup, "id", false, new UserGroupDto { Id = 5, StartMediaId = -1, StartContentId = -1, Alias = Constants.Security.SensitiveDataGroupAlias, Name = "Sensitive data", DefaultPermissions = "", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-lock" });
}
private void CreateUser2UserGroupData()
{
_database.Insert(new User2UserGroupDto { UserGroupId = 1, UserId = Constants.Security.SuperId });
_database.Insert(new User2UserGroupDto { UserGroupId = 1, UserId = Constants.Security.SuperId }); // add super to admins
_database.Insert(new User2UserGroupDto { UserGroupId = 5, UserId = Constants.Security.SuperId }); // add super to sensitive data
}
private void CreateUserGroup2AppData()
@@ -28,71 +28,60 @@ namespace Umbraco.Core.Migrations.Install
private ISqlSyntaxProvider SqlSyntax => _database.SqlContext.SqlSyntax;
// all tables, in order
public static readonly Dictionary<int, Type> OrderedTables = new Dictionary<int, Type>
public static readonly List<Type> OrderedTables = new List<Type>
{
{0, typeof (NodeDto)},
{1, typeof (ContentTypeDto)},
{2, typeof (TemplateDto)},
{3, typeof (ContentDto)},
{4, typeof (ContentVersionDto)},
{5, typeof (DocumentDto)},
{6, typeof (ContentTypeTemplateDto)},
{7, typeof (DataTypeDto)},
//removed: {8, typeof (DataTypePreValueDto)},
{9, typeof (DictionaryDto)},
{10, typeof (LanguageDto)},
{11, typeof (LanguageTextDto)},
{12, typeof (DomainDto)},
{13, typeof (LogDto)},
{14, typeof (MacroDto)},
{15, typeof (MacroPropertyDto)},
{16, typeof (MemberTypeDto)},
{17, typeof (MemberDto)},
{18, typeof (Member2MemberGroupDto)},
{19, typeof (ContentXmlDto)},
{20, typeof (PreviewXmlDto)},
{21, typeof (PropertyTypeGroupDto)},
{22, typeof (PropertyTypeDto)},
{23, typeof (PropertyDataDto)},
{24, typeof (RelationTypeDto)},
{25, typeof (RelationDto)},
//removed: {26...
//removed: {27...
{28, typeof (TagDto)},
{29, typeof (TagRelationshipDto)},
//removed: {30...
//removed in 7.6: {31, typeof (UserTypeDto)},
{32, typeof (UserDto)},
{33, typeof (TaskTypeDto)},
{34, typeof (TaskDto)},
{35, typeof (ContentType2ContentTypeDto)},
{36, typeof (ContentTypeAllowedContentTypeDto)},
//removed in 7.6: {37, typeof (User2AppDto)},
{38, typeof (User2NodeNotifyDto)},
//removed in 7.6: {39, typeof (User2NodePermissionDto)},
{40, typeof (ServerRegistrationDto)},
{41, typeof (AccessDto)},
{42, typeof (AccessRuleDto)},
{43, typeof (CacheInstructionDto)},
{44, typeof (ExternalLoginDto)},
//removed: {45, typeof (MigrationDto)},
//removed: {46, typeof (UmbracoDeployChecksumDto)},
//removed: {47, typeof (UmbracoDeployDependencyDto)},
{48, typeof (RedirectUrlDto) },
{49, typeof (LockDto) },
{50, typeof (UserGroupDto) },
{51, typeof (User2UserGroupDto) },
{52, typeof (UserGroup2NodePermissionDto) },
{53, typeof (UserGroup2AppDto) },
{54, typeof (UserStartNodeDto) },
{55, typeof (ContentNuDto) },
{56, typeof (DocumentVersionDto) },
{57, typeof (KeyValueDto) }
typeof (NodeDto),
typeof (ContentTypeDto),
typeof (TemplateDto),
typeof (ContentDto),
typeof (ContentVersionDto),
typeof (MediaVersionDto),
typeof (DocumentDto),
typeof (ContentTypeTemplateDto),
typeof (DataTypeDto),
typeof (DictionaryDto),
typeof (LanguageDto),
typeof (LanguageTextDto),
typeof (DomainDto),
typeof (LogDto),
typeof (MacroDto),
typeof (MacroPropertyDto),
typeof (MemberTypeDto),
typeof (MemberDto),
typeof (Member2MemberGroupDto),
typeof (ContentXmlDto),
typeof (PreviewXmlDto),
typeof (PropertyTypeGroupDto),
typeof (PropertyTypeDto),
typeof (PropertyDataDto),
typeof (RelationTypeDto),
typeof (RelationDto),
typeof (TagDto),
typeof (TagRelationshipDto),
typeof (UserDto),
typeof (TaskTypeDto),
typeof (TaskDto),
typeof (ContentType2ContentTypeDto),
typeof (ContentTypeAllowedContentTypeDto),
typeof (User2NodeNotifyDto),
typeof (ServerRegistrationDto),
typeof (AccessDto),
typeof (AccessRuleDto),
typeof (CacheInstructionDto),
typeof (ExternalLoginDto),
typeof (RedirectUrlDto),
typeof (LockDto),
typeof (UserGroupDto),
typeof (User2UserGroupDto),
typeof (UserGroup2NodePermissionDto),
typeof (UserGroup2AppDto),
typeof (UserStartNodeDto),
typeof (ContentNuDto),
typeof (DocumentVersionDto),
typeof (KeyValueDto),
typeof (UserLoginDto),
typeof (ConsentDto),
typeof (AuditEntryDto)
};
/// <summary>
@@ -102,11 +91,10 @@ namespace Umbraco.Core.Migrations.Install
{
_logger.Info<DatabaseSchemaCreator>("Start UninstallDatabaseSchema");
foreach (var item in OrderedTables.OrderByDescending(x => x.Key))
foreach (var table in OrderedTables.AsEnumerable().Reverse())
{
var tableNameAttribute = item.Value.FirstAttribute<TableNameAttribute>();
var tableName = tableNameAttribute == null ? item.Value.Name : tableNameAttribute.Value;
var tableNameAttribute = table.FirstAttribute<TableNameAttribute>();
var tableName = tableNameAttribute == null ? table.Name : tableNameAttribute.Value;
_logger.Info<DatabaseSchemaCreator>("Uninstall" + tableName);
@@ -135,8 +123,8 @@ namespace Umbraco.Core.Migrations.Install
if (e.Cancel == false)
{
var dataCreation = new DatabaseDataCreator(_database, _logger);
foreach (var item in OrderedTables.OrderBy(x => x.Key))
CreateTable(false, item.Value, dataCreation);
foreach (var table in OrderedTables)
CreateTable(false, table, dataCreation);
}
FireAfterCreation(e);
@@ -160,8 +148,7 @@ namespace Umbraco.Core.Migrations.Install
}).ToArray();
result.TableDefinitions.AddRange(OrderedTables
.OrderBy(x => x.Key)
.Select(x => DefinitionFactory.GetTableDefinition(x.Value, SqlSyntax)));
.Select(x => DefinitionFactory.GetTableDefinition(x, SqlSyntax)));
ValidateDbTables(result);
ValidateDbColumns(result);
@@ -136,6 +136,18 @@ namespace Umbraco.Core.Migrations.Install
return new Version(7, 6, 0);
}
//if the error is for cmsMedia it must be the previous version to 7.8 since that is when it is added
if (Errors.Any(x => x.Item1.Equals("Table") && (x.Item2.InvariantEquals("umbracoMedia"))))
{
return new Version(7, 7, 0);
}
//if the error is for isSensitive column it must be the previous version to 7.9 since that is when it is added
if (Errors.Any(x => x.Item1.Equals("Column") && (x.Item2.InvariantEquals("cmsMemberType,isSensitive"))))
{
return new Version(7, 8, 0);
}
return UmbracoVersion.Current;
}
@@ -11,6 +11,14 @@ namespace Umbraco.Core.Migrations
{
// provides extra methods for migrations
protected void AddColumn<T>(string columnName)
{
var table = DefinitionFactory.GetTableDefinition(typeof(T), SqlSyntax);
var column = table.Columns.First(x => x.Name == columnName);
var createSql = SqlSyntax.Format(column);
Database.Execute(string.Format(SqlSyntax.AddColumn, SqlSyntax.GetQuotedTableName(table.Name), createSql));
}
protected void AddColumn<T>(string tableName, string columnName)
{
//if (ColumnExists(tableName, columnName))
@@ -7,6 +7,8 @@ using Umbraco.Core.Migrations.Upgrade.V_7_5_0;
using Umbraco.Core.Migrations.Upgrade.V_7_5_5;
using Umbraco.Core.Migrations.Upgrade.V_7_6_0;
using Umbraco.Core.Migrations.Upgrade.V_7_7_0;
using Umbraco.Core.Migrations.Upgrade.V_7_8_0;
using Umbraco.Core.Migrations.Upgrade.V_7_9_0;
using Umbraco.Core.Migrations.Upgrade.V_8_0_0;
namespace Umbraco.Core.Migrations.Upgrade
@@ -36,8 +38,8 @@ namespace Umbraco.Core.Migrations.Upgrade
if (!SemVersion.TryParse(ConfigurationManager.AppSettings["umbracoConfigurationStatus"], out var currentVersion))
throw new InvalidOperationException("Could not get current version from web.config umbracoConfigurationStatus appSetting.");
// must be at least 7.8.0 - fixme adjust when releasing
if (currentVersion < new SemVersion(7, 8))
// must be at least 7.?.? - fixme adjust when releasing
if (currentVersion < new SemVersion(7, 999))
throw new InvalidOperationException($"Version {currentVersion} cannot be upgraded to {UmbracoVersion.SemanticVersion}.");
// cannot go back in time
@@ -60,12 +62,6 @@ namespace Umbraco.Core.Migrations.Upgrade
private void DefinePlan()
{
// INSTALL
//
// when installing, the source state is empty, and the target state should be the final state.
Add(string.Empty, "{CA7DB949-3EF4-403D-8464-F9BA36A52E87}");
// UPGRADE FROM 7
//
// when 8.0.0 is released, on the first upgrade, the state is automatically
@@ -74,45 +70,38 @@ namespace Umbraco.Core.Migrations.Upgrade
// then, as more v7 and v8 versions are released, new chains needs to be defined to
// support the upgrades (new v7 may backport some migrations and require their own
// upgrade paths, etc).
// fixme adjust when releasing
From("{init-7.8.0}")
.Chain<V_8_0_0.AddLockObjects>("{7C447271-CA3F-4A6A-A913-5D77015655CB}") // add more lock objects
.Chain<AddContentNuTable>("{CBFF58A2-7B50-4F75-8E98-249920DB0F37}")
.Chain<RefactorXmlColumns>("{3D18920C-E84D-405C-A06A-B7CEE52FE5DD}")
.Chain<VariantsMigration>("{FB0A5429-587E-4BD0-8A67-20F0E7E62FF7}")
.Chain<DropMigrationsTable>("{F0C42457-6A3B-4912-A7EA-F27ED85A2092}")
.Chain<DataTypeMigration>("{8640C9E4-A1C0-4C59-99BB-609B4E604981}")
.Chain<TagsMigration>("{DD1B99AF-8106-4E00-BAC7-A43003EA07F8}")
.Chain<SuperZero>("{9DF05B77-11D1-475C-A00A-B656AF7E0908}")
.Chain<PropertyEditorsMigration>("{CA7DB949-3EF4-403D-8464-F9BA36A52E87}");;
From("{init-7.8.0}");
Chain<V_8_0_0.AddLockObjects>("{7C447271-CA3F-4A6A-A913-5D77015655CB}"); // add more lock objects
Chain<AddContentNuTable>("{CBFF58A2-7B50-4F75-8E98-249920DB0F37}");
Chain<RefactorXmlColumns>("{3D18920C-E84D-405C-A06A-B7CEE52FE5DD}");
Chain<VariantsMigration>("{FB0A5429-587E-4BD0-8A67-20F0E7E62FF7}");
Chain<DropMigrationsTable>("{F0C42457-6A3B-4912-A7EA-F27ED85A2092}");
Chain<DataTypeMigration>("{8640C9E4-A1C0-4C59-99BB-609B4E604981}");
Chain<TagsMigration>("{DD1B99AF-8106-4E00-BAC7-A43003EA07F8}");
Chain<SuperZero>("{9DF05B77-11D1-475C-A00A-B656AF7E0908}");
Chain<PropertyEditorsMigration>("{CA7DB949-3EF4-403D-8464-F9BA36A52E87}");
// 7.8.1 = same as 7.8.0
From("{init-7.8.1}")
.Chain("{init-7.8.0}");
From("{init-7.8.1}");
Chain("{init-7.8.0}");
// 7.9.0 = requires its own chain
From("{init-7.9.0}")
// chain...
.Chain("{82C4BA1D-7720-46B1-BBD7-07F3F73800E6}");
From("{init-7.9.0}");
// chain...
Chain("{82C4BA1D-7720-46B1-BBD7-07F3F73800E6}");
// UPGRADE 8
//
// starting from the original 8.0.0 final state, chain migrations to upgrade version 8,
// defining new final states as more migrations are added to the chain.
//From("")
// .Chain("")
// .Chain("");
// WIP 8
//
// before v8 is released, some sites may exist, and these "pre-8" versions require their
// own upgrade plan. in other words, this is the plan for sites that were on v8 before
// own upgrade plan. in other words, this is also the plan for sites that were on v8 before
// v8 was released
// fixme - this is essentially for ZpqrtBnk website
// need to determine which version it is and where it should resume running migrations
// 8.0.0
From("{init-origin}");
Chain<V_8_0_0.AddLockTable>("{98347B5E-65BF-4DD7-BB43-A09CB7AF4FCA}");
@@ -129,8 +118,6 @@ namespace Umbraco.Core.Migrations.Upgrade
Chain<UpdateAllowedMediaTypesAtRoot>("{44484C32-EEB3-4A12-B1CB-11E02CE22AB2}");
// 7.6.0
//Chain<AddLockTable>("{858B4039-070C-4928-BBEC-DDE8303352DA}");
//Chain<AddLockObjects>("{64F587C1-0B28-4D78-B4CC-26B7D87F69C1}");
Chain<AddIndexesToUmbracoRelationTables>("{3586E4E9-2922-49EB-8E2A-A530CE6DBDE0}");
Chain<AddIndexToCmsMemberLoginName>("{D4A5674F-654D-4CC7-85E5-CFDBC533A318}");
Chain<AddIndexToUmbracoNodePath>("{7F828EDD-6622-4A8D-AD80-EEAF46C11680}");
@@ -156,6 +143,33 @@ namespace Umbraco.Core.Migrations.Upgrade
Chain<TagsMigration>("{139F26D7-7E08-48E3-81D9-E50A21A72F67}");
Chain<SuperZero>("{CC1B1201-1328-443C-954A-E0BBB8CCC1B5}");
Chain<PropertyEditorsMigration>("{CA7DB949-3EF4-403D-8464-F9BA36A52E87}");
// at this point of the chain, people started to work on v8, so whenever we
// merge stuff from v7, we have to chain the migrations here so they also
// run for v8.
// mergin from 7.8.0
Chain<AddUserLoginTable>("{FDCB727A-EFB6-49F3-89E4-A346503AB849}");
Chain<AddTourDataUserColumn>("{2A796A08-4FE4-4783-A1A5-B8A6C8AA4A92}");
Chain<AddMediaVersionTable>("{1A46A98B-2AAB-4C8E-870F-A2D55A97FD1F}");
Chain<AddInstructionCountColumn>("{0AE053F6-2683-4234-87B2-E963F8CE9498}");
Chain<AddIndexToPropertyTypeAliasColumn>("{D454541C-15C5-41CF-8109-937F26A78E71}");
// merging from 7.9.0
Chain<AddIsSensitiveMemberTypeColumn>("{89A728D1-FF4C-4155-A269-62CC09AD2131}");
Chain<AddUmbracoAuditTable>("{FD8631BC-0388-425C-A451-5F58574F6F05}");
Chain<AddUmbracoConsentTable>("{2821F53E-C58B-4812-B184-9CD240F990D7}");
Chain<CreateSensitiveDataUserGroup>("{8918450B-3DA0-4BB7-886A-6FA8B7E4186E}"); // final state
// BEWARE! whenever changing the final state, update below!
// INSTALL
//
// when installing, the source state is empty, and the target state should be the final state.
// BEWARE! this MUST match the final state above!
Add(string.Empty, "{8918450B-3DA0-4BB7-886A-6FA8B7E4186E}");
}
}
}
@@ -0,0 +1,27 @@
using System.Linq;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Migrations.Upgrade.V_7_8_0
{
internal class AddIndexToPropertyTypeAliasColumn : MigrationBase
{
public AddIndexToPropertyTypeAliasColumn(IMigrationContext context)
: base(context)
{ }
public override void Migrate()
{
var dbIndexes = SqlSyntax.GetDefinedIndexesDefinitions(Context.Database);
//make sure it doesn't already exist
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsPropertyTypeAlias")) == false)
{
//we can apply the index
Create.Index("IX_cmsPropertyTypeAlias").OnTable(Constants.DatabaseSchema.Tables.PropertyType)
.OnColumn("Alias")
.Ascending().WithOptions().NonClustered()
.Do();
}
}
}
}
@@ -0,0 +1,20 @@
using System.Linq;
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Migrations.Upgrade.V_7_8_0
{
internal class AddInstructionCountColumn : MigrationBase
{
public AddInstructionCountColumn(IMigrationContext context)
: base(context)
{ }
public override void Migrate()
{
var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray();
if (columns.Any(x => x.TableName.InvariantEquals(Constants.DatabaseSchema.Tables.CacheInstruction) && x.ColumnName.InvariantEquals("instructionCount")) == false)
AddColumn<CacheInstructionDto>("instructionCount");
}
}
}
@@ -0,0 +1,65 @@
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.Persistence.Factories;
using static Umbraco.Core.Persistence.NPocoSqlExtensions.Statics;
namespace Umbraco.Core.Migrations.Upgrade.V_7_8_0
{
internal class AddMediaVersionTable : MigrationBase
{
public AddMediaVersionTable(IMigrationContext context)
: base(context)
{ }
public override void Migrate()
{
var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray();
if (tables.InvariantContains(Constants.DatabaseSchema.Tables.MediaVersion)) return;
Create.Table<MediaVersionDto>().Do();
MigrateMediaPaths();
}
private void MigrateMediaPaths()
{
// this may not be the most efficient way to do it, compared to how it's done in v7, but this
// migration should only run for v8 sites that are being developed, before v8 is released, so
// no big sites and performances don't matter here - keep it simple
var sql = Sql()
.Select<PropertyDataDto>(x => x.VarcharValue, x => x.TextValue)
.AndSelect<ContentVersionDto>(x => Alias(x.Id, "versionId"))
.From<PropertyDataDto>()
.InnerJoin<PropertyTypeDto>().On<PropertyDataDto, PropertyTypeDto>((left, right) => left.PropertyTypeId == right.Id)
.InnerJoin<ContentVersionDto>().On<PropertyDataDto, ContentVersionDto>((left, right) => left.VersionId == right.Id)
.InnerJoin<NodeDto>().On<ContentVersionDto, NodeDto>((left, right) => left.NodeId == right.NodeId)
.Where<PropertyTypeDto>(x => x.Alias == "umbracoFile")
.Where<NodeDto>(x => x.NodeObjectType == Constants.ObjectTypes.Media);
var paths = new List<MediaVersionDto>();
//using QUERY = a db cursor, we won't load this all into memory first, just row by row
foreach (var row in Database.Query<dynamic>(sql))
{
// if there's values then ensure there's a media path match and extract it
string mediaPath = null;
if (
(row.varcharValue != null && ContentBaseFactory.TryMatch((string) row.varcharValue, out mediaPath))
|| (row.textValue != null && ContentBaseFactory.TryMatch((string) row.textValue, out mediaPath)))
{
paths.Add(new MediaVersionDto
{
Id = (int) row.versionId,
Path = mediaPath
});
}
}
// bulk insert
Database.BulkInsertRecords(paths);
}
}
}
@@ -0,0 +1,21 @@
using System.Linq;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Migrations.Upgrade.V_7_8_0
{
internal class AddTourDataUserColumn : MigrationBase
{
public AddTourDataUserColumn(IMigrationContext context)
: base(context)
{ }
public override void Migrate()
{
var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray();
if (columns.Any(x => x.TableName.InvariantEquals(Constants.DatabaseSchema.Tables.User) && x.ColumnName.InvariantEquals("tourData")) == false)
AddColumn<UserDto>("tourData");
}
}
}
@@ -0,0 +1,22 @@
using System.Linq;
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Migrations.Upgrade.V_7_8_0
{
internal class AddUserLoginTable : MigrationBase
{
public AddUserLoginTable(IMigrationContext context)
: base(context)
{ }
public override void Migrate()
{
var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray();
if (tables.InvariantContains(Constants.DatabaseSchema.Tables.UserLogin) == false)
{
Create.Table<UserLoginDto>().Do();
}
}
}
}
@@ -0,0 +1,20 @@
using System.Linq;
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Migrations.Upgrade.V_7_9_0
{
internal class AddIsSensitiveMemberTypeColumn : MigrationBase
{
public AddIsSensitiveMemberTypeColumn(IMigrationContext context)
: base(context)
{ }
public override void Migrate()
{
var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray();
if (columns.Any(x => x.TableName.InvariantEquals(Constants.DatabaseSchema.Tables.MemberType) && x.ColumnName.InvariantEquals("isSensitive")) == false)
AddColumn<MemberTypeDto>("isSensitive");
}
}
}
@@ -0,0 +1,22 @@
using System.Linq;
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Migrations.Upgrade.V_7_9_0
{
internal class AddUmbracoAuditTable : MigrationBase
{
public AddUmbracoAuditTable(IMigrationContext context)
: base(context)
{ }
public override void Migrate()
{
var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray();
if (tables.InvariantContains(Constants.DatabaseSchema.Tables.AuditEntry))
return;
Create.Table<AuditEntryDto>().Do();
}
}
}
@@ -0,0 +1,22 @@
using System.Linq;
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Migrations.Upgrade.V_7_9_0
{
internal class AddUmbracoConsentTable : MigrationBase
{
public AddUmbracoConsentTable(IMigrationContext context)
: base(context)
{ }
public override void Migrate()
{
var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray();
if (tables.InvariantContains(Constants.DatabaseSchema.Tables.Consent))
return;
Create.Table<ConsentDto>().Do();
}
}
}
@@ -0,0 +1,27 @@
using System;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Migrations.Upgrade.V_7_9_0
{
internal class CreateSensitiveDataUserGroup : MigrationBase
{
public CreateSensitiveDataUserGroup(IMigrationContext context)
: base(context)
{ }
public override void Migrate()
{
var sql = Sql()
.SelectCount()
.From<UserGroupDto>()
.Where<UserGroupDto>(x => x.Alias == Constants.Security.SensitiveDataGroupAlias);
var exists = Database.ExecuteScalar<int>(sql) > 0;
if (exists) return;
var groupId = Database.Insert(Constants.DatabaseSchema.Tables.UserGroup, "id", new UserGroupDto { StartMediaId = -1, StartContentId = -1, Alias = Constants.Security.SensitiveDataGroupAlias, Name = "Sensitive data", DefaultPermissions = "", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-lock" });
Database.Insert(new User2UserGroupDto { UserGroupId = Convert.ToInt32(groupId), UserId = Constants.Security.SuperId }); // add super to sensitive data
}
}
}
@@ -35,7 +35,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
// re-create *all* keys and indexes
foreach (var x in DatabaseSchemaCreator.OrderedTables)
Create.KeysAndIndexes(x.Value).Do();
Create.KeysAndIndexes(x).Do();
// renames
Database.Execute(Sql()
@@ -52,7 +52,7 @@ HAVING COUNT(v2.id) <> 1").Any())
// re-create *all* keys and indexes
foreach (var x in DatabaseSchemaCreator.OrderedTables)
Create.KeysAndIndexes(x.Value).Do();
Create.KeysAndIndexes(x).Do();
}
private void MigratePropertyData()
+94
View File
@@ -0,0 +1,94 @@
using System;
using System.Reflection;
using System.Runtime.Serialization;
using Umbraco.Core.Models.Entities;
namespace Umbraco.Core.Models
{
/// <summary>
/// Represents an audited event.
/// </summary>
[Serializable]
[DataContract(IsReference = true)]
internal class AuditEntry : EntityBase, IAuditEntry
{
private static PropertySelectors _selectors;
private int _performingUserId;
private string _performingDetails;
private string _performingIp;
private int _affectedUserId;
private string _affectedDetails;
private string _eventType;
private string _eventDetails;
private static PropertySelectors Selectors => _selectors ?? (_selectors = new PropertySelectors());
private class PropertySelectors
{
public readonly PropertyInfo PerformingUserId = ExpressionHelper.GetPropertyInfo<AuditEntry, int>(x => x.PerformingUserId);
public readonly PropertyInfo PerformingDetails = ExpressionHelper.GetPropertyInfo<AuditEntry, string>(x => x.PerformingDetails);
public readonly PropertyInfo PerformingIp = ExpressionHelper.GetPropertyInfo<AuditEntry, string>(x => x.PerformingIp);
public readonly PropertyInfo AffectedUserId = ExpressionHelper.GetPropertyInfo<AuditEntry, int>(x => x.AffectedUserId);
public readonly PropertyInfo AffectedDetails = ExpressionHelper.GetPropertyInfo<AuditEntry, string>(x => x.AffectedDetails);
public readonly PropertyInfo EventType = ExpressionHelper.GetPropertyInfo<AuditEntry, string>(x => x.EventType);
public readonly PropertyInfo EventDetails = ExpressionHelper.GetPropertyInfo<AuditEntry, string>(x => x.EventDetails);
}
/// <inheritdoc />
public int PerformingUserId
{
get => _performingUserId;
set => SetPropertyValueAndDetectChanges(value, ref _performingUserId, Selectors.PerformingUserId);
}
/// <inheritdoc />
public string PerformingDetails
{
get => _performingDetails;
set => SetPropertyValueAndDetectChanges(value, ref _performingDetails, Selectors.PerformingDetails);
}
/// <inheritdoc />
public string PerformingIp
{
get => _performingIp;
set => SetPropertyValueAndDetectChanges(value, ref _performingIp, Selectors.PerformingIp);
}
/// <inheritdoc />
public DateTime EventDateUtc
{
get => CreateDate;
set => CreateDate = value;
}
/// <inheritdoc />
public int AffectedUserId
{
get => _affectedUserId;
set => SetPropertyValueAndDetectChanges(value, ref _affectedUserId, Selectors.AffectedUserId);
}
/// <inheritdoc />
public string AffectedDetails
{
get => _affectedDetails;
set => SetPropertyValueAndDetectChanges(value, ref _affectedDetails, Selectors.AffectedDetails);
}
/// <inheritdoc />
public string EventType
{
get => _eventType;
set => SetPropertyValueAndDetectChanges(value, ref _eventType, Selectors.EventType);
}
/// <inheritdoc />
public string EventDetails
{
get => _eventDetails;
set => SetPropertyValueAndDetectChanges(value, ref _eventDetails, Selectors.EventDetails);
}
}
}
+15 -4
View File
@@ -2,18 +2,29 @@
namespace Umbraco.Core.Models
{
public sealed class AuditItem : EntityBase
public sealed class AuditItem : EntityBase, IAuditItem
{
/// <summary>
/// Constructor for creating an item to be created
/// </summary>
/// <param name="objectId"></param>
/// <param name="comment"></param>
/// <param name="type"></param>
/// <param name="userId"></param>
public AuditItem(int objectId, string comment, AuditType type, int userId)
{
DisableChangeTracking();
Id = objectId;
Comment = comment;
AuditType = type;
UserId = userId;
EnableChangeTracking();
}
public string Comment { get; private set; }
public AuditType AuditType { get; private set; }
public int UserId { get; private set; }
public string Comment { get; }
public AuditType AuditType { get; }
public int UserId { get; }
}
}
+102
View File
@@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
using Umbraco.Core.Models.Entities;
namespace Umbraco.Core.Models
{
/// <summary>
/// Represents a consent.
/// </summary>
[Serializable]
[DataContract(IsReference = true)]
internal class Consent : EntityBase, IConsent
{
private static PropertySelectors _selector;
private bool _current;
private string _source;
private string _context;
private string _action;
private ConsentState _state;
private string _comment;
// ReSharper disable once ClassNeverInstantiated.Local
private class PropertySelectors
{
public readonly PropertyInfo Current = ExpressionHelper.GetPropertyInfo<Consent, bool>(x => x.Current);
public readonly PropertyInfo Source = ExpressionHelper.GetPropertyInfo<Consent, string>(x => x.Source);
public readonly PropertyInfo Context = ExpressionHelper.GetPropertyInfo<Consent, string>(x => x.Context);
public readonly PropertyInfo Action = ExpressionHelper.GetPropertyInfo<Consent, string>(x => x.Action);
public readonly PropertyInfo State = ExpressionHelper.GetPropertyInfo<Consent, ConsentState>(x => x.State);
public readonly PropertyInfo Comment = ExpressionHelper.GetPropertyInfo<Consent, string>(x => x.Comment);
}
private static PropertySelectors Selectors => _selector ?? (_selector = new PropertySelectors());
/// <inheritdoc />
public bool Current
{
get => _current;
set => SetPropertyValueAndDetectChanges(value, ref _current, Selectors.Current);
}
/// <inheritdoc />
public string Source
{
get => _source;
set
{
if (string.IsNullOrWhiteSpace(value)) throw new ArgumentException(nameof(value));
SetPropertyValueAndDetectChanges(value, ref _source, Selectors.Source);
}
}
/// <inheritdoc />
public string Context
{
get => _context;
set
{
if (string.IsNullOrWhiteSpace(value)) throw new ArgumentException(nameof(value));
SetPropertyValueAndDetectChanges(value, ref _context, Selectors.Context);
}
}
/// <inheritdoc />
public string Action
{
get => _action;
set
{
if (string.IsNullOrWhiteSpace(value)) throw new ArgumentException(nameof(value));
SetPropertyValueAndDetectChanges(value, ref _action, Selectors.Action);
}
}
/// <inheritdoc />
public ConsentState State
{
get => _state;
// note: we probably should validate the state here, but since the
// enum is [Flags] with many combinations, this could be expensive
set => SetPropertyValueAndDetectChanges(value, ref _state, Selectors.State);
}
/// <inheritdoc />
public string Comment
{
get => _comment;
set => SetPropertyValueAndDetectChanges(value, ref _comment, Selectors.Comment);
}
/// <inheritdoc />
public IEnumerable<IConsent> History => HistoryInternal;
/// <summary>
/// Gets the previous states of this consent.
/// </summary>
internal List<IConsent> HistoryInternal { get; set; }
}
}
@@ -0,0 +1,18 @@
namespace Umbraco.Core.Models
{
/// <summary>
/// Provides extension methods for the <see cref="IConsent"/> interface.
/// </summary>
public static class ConsentExtensions
{
/// <summary>
/// Determines whether the consent is granted.
/// </summary>
public static bool IsGranted(this IConsent consent) => (consent.State & ConsentState.Granted) > 0;
/// <summary>
/// Determines whether the consent is revoked.
/// </summary>
public static bool IsRevoked(this IConsent consent) => (consent.State & ConsentState.Revoked) > 0;
}
}
+38
View File
@@ -0,0 +1,38 @@
using System;
namespace Umbraco.Core.Models
{
/// <summary>
/// Represents the state of a consent.
/// </summary>
[Flags]
public enum ConsentState // : int
{
// note - this is a [Flags] enumeration
// on can create detailed flags such as:
//GrantedOptIn = Granted | 0x0001
//GrandedByForce = Granted | 0x0002
//
// 16 situations for each Pending/Granted/Revoked should be ok
/// <summary>
/// There is no consent.
/// </summary>
None = 0,
/// <summary>
/// Consent is pending and has not been granted yet.
/// </summary>
Pending = 0x10000,
/// <summary>
/// Consent has been granted.
/// </summary>
Granted = 0x20000,
/// <summary>
/// Consent has been revoked.
/// </summary>
Revoked = 0x40000
}
}
+28 -15
View File
@@ -223,9 +223,8 @@ namespace Umbraco.Core.Models
#region Dirty
/// <summary>
/// Resets dirty properties.
/// </summary>
/// <inheritdoc />
/// <remarks>Overriden to include user properties.</remarks>
public override void ResetDirtyProperties(bool rememberDirty)
{
base.ResetDirtyProperties(rememberDirty);
@@ -235,17 +234,15 @@ namespace Umbraco.Core.Models
prop.ResetDirtyProperties(rememberDirty);
}
/// <summary>
/// Gets a value indicating whether the current entity is dirty.
/// </summary>
/// <inheritdoc />
/// <remarks>Overriden to include user properties.</remarks>
public override bool IsDirty()
{
return IsEntityDirty() || this.IsAnyUserPropertyDirty();
}
/// <summary>
/// Gets a value indicating whether the current entity was dirty.
/// </summary>
/// <inheritdoc />
/// <remarks>Overriden to include user properties.</remarks>
public override bool WasDirty()
{
return WasEntityDirty() || this.WasAnyUserPropertyDirty();
@@ -267,9 +264,8 @@ namespace Umbraco.Core.Models
return base.WasDirty();
}
/// <summary>
/// Gets a value indicating whether a user property is dirty.
/// </summary>
/// <inheritdoc />
/// <remarks>Overriden to include user properties.</remarks>
public override bool IsPropertyDirty(string propertyName)
{
if (base.IsPropertyDirty(propertyName))
@@ -278,9 +274,8 @@ namespace Umbraco.Core.Models
return Properties.Contains(propertyName) && Properties[propertyName].IsDirty();
}
/// <summary>
/// Gets a value indicating whether a user property was dirty.
/// </summary>
/// <inheritdoc />
/// <remarks>Overriden to include user properties.</remarks>
public override bool WasPropertyDirty(string propertyName)
{
if (base.WasPropertyDirty(propertyName))
@@ -289,6 +284,24 @@ namespace Umbraco.Core.Models
return Properties.Contains(propertyName) && Properties[propertyName].WasDirty();
}
/// <inheritdoc />
/// <remarks>Overriden to include user properties.</remarks>
public override IEnumerable<string> GetDirtyProperties()
{
var instanceProperties = base.GetDirtyProperties();
var propertyTypes = Properties.Where(x => x.IsDirty()).Select(x => x.Alias);
return instanceProperties.Concat(propertyTypes);
}
/// <inheritdoc />
/// <remarks>Overriden to include user properties.</remarks>
public override IEnumerable<string> GetWereDirtyProperties()
{
var instanceProperties = base.GetWereDirtyProperties();
var propertyTypes = Properties.Where(x => x.WasDirty()).Select(x => x.Alias);
return instanceProperties.Concat(propertyTypes);
}
#endregion
}
}
+5 -3
View File
@@ -97,11 +97,13 @@ namespace Umbraco.Core.Models
[DataMember]
public IEnumerable<ITemplate> AllowedTemplates
{
get { return _allowedTemplates; }
get => _allowedTemplates;
set
{
SetPropertyValueAndDetectChanges(value, ref _allowedTemplates, Ps.Value.AllowedTemplatesSelector,
Ps.Value.TemplateComparer);
SetPropertyValueAndDetectChanges(value, ref _allowedTemplates, Ps.Value.AllowedTemplatesSelector, Ps.Value.TemplateComparer);
if (_allowedTemplates.Any(x => x.Id == _defaultTemplate) == false)
DefaultTemplateId = 0;
}
}
+60
View File
@@ -0,0 +1,60 @@
using System;
using Umbraco.Core.Models.Entities;
namespace Umbraco.Core.Models
{
/// <summary>
/// Represents an audited event.
/// </summary>
/// <remarks>
/// <para>The free-form details properties can be used to capture relevant infos (for example,
/// a user email and identifier) at the time of the audited event, even though they may change
/// later on - but we want to keep a track of their value at that time.</para>
/// <para>Depending on audit loggers, these properties can be purely free-form text, or
/// contain json serialized objects.</para>
/// </remarks>
public interface IAuditEntry : IEntity, IRememberBeingDirty
{
/// <summary>
/// Gets or sets the identifier of the user triggering the audited event.
/// </summary>
int PerformingUserId { get; set; }
/// <summary>
/// Gets or sets free-form details about the user triggering the audited event.
/// </summary>
string PerformingDetails { get; set; }
/// <summary>
/// Gets or sets the IP address or the request triggering the audited event.
/// </summary>
string PerformingIp { get; set; }
/// <summary>
/// Gets or sets the date and time of the audited event.
/// </summary>
DateTime EventDateUtc { get; set; }
/// <summary>
/// Gets or sets the identifier of the user affected by the audited event.
/// </summary>
/// <remarks>Not used when no single user is affected by the event.</remarks>
int AffectedUserId { get; set; }
/// <summary>
/// Gets or sets free-form details about the entity affected by the audited event.
/// </summary>
/// <remarks>The entity affected by the event can be another user, a member...</remarks>
string AffectedDetails { get; set; }
/// <summary>
/// Gets or sets the type of the audited event.
/// </summary>
string EventType { get; set; }
/// <summary>
/// Gets or sets free-form details about the audited event.
/// </summary>
string EventDetails { get; set; }
}
}
+12
View File
@@ -0,0 +1,12 @@
using System;
using Umbraco.Core.Models.Entities;
namespace Umbraco.Core.Models
{
public interface IAuditItem : IEntity
{
string Comment { get; }
AuditType AuditType { get; }
int UserId { get; }
}
}
+55
View File
@@ -0,0 +1,55 @@
using System.Collections.Generic;
using Umbraco.Core.Models.Entities;
namespace Umbraco.Core.Models
{
/// <summary>
/// Represents a consent state.
/// </summary>
/// <remarks>
/// <para>A consent is fully identified by a source (whoever is consenting), a context (for
/// example, an application), and an action (whatever is consented).</para>
/// <para>A consent state registers the state of the consent (granted, revoked...).</para>
/// </remarks>
public interface IConsent : IEntity, IRememberBeingDirty
{
/// <summary>
/// Determines whether the consent entity represents the current state.
/// </summary>
bool Current { get; }
/// <summary>
/// Gets the unique identifier of whoever is consenting.
/// </summary>
string Source { get; }
/// <summary>
/// Gets the unique identifier of the context of the consent.
/// </summary>
/// <remarks>
/// <para>Represents the domain, application, scope... of the action.</para>
/// <para>When the action is a Udi, this should be the Udi type.</para>
/// </remarks>
string Context { get; }
/// <summary>
/// Gets the unique identifier of the consented action.
/// </summary>
string Action { get; }
/// <summary>
/// Gets the state of the consent.
/// </summary>
ConsentState State { get; }
/// <summary>
/// Gets some additional free text.
/// </summary>
string Comment { get; }
/// <summary>
/// Gets the previous states of this consent.
/// </summary>
IEnumerable<IConsent> History { get; }
}
}
+14
View File
@@ -19,6 +19,13 @@
/// <returns></returns>
bool MemberCanViewProperty(string propertyTypeAlias);
/// <summary>
/// Gets a boolean indicating whether a Property is marked as storing sensitive values on the Members profile.
/// </summary>
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to check</param>
/// <returns></returns>
bool IsSensitiveProperty(string propertyTypeAlias);
/// <summary>
/// Sets a boolean indicating whether a Property is editable by the Member.
/// </summary>
@@ -32,5 +39,12 @@
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to set</param>
/// <param name="value">Boolean value, true or false</param>
void SetMemberCanViewProperty(string propertyTypeAlias, bool value);
/// <summary>
/// Sets a boolean indicating whether a Property is a sensitive value on the Members profile.
/// </summary>
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to set</param>
/// <param name="value">Boolean value, true or false</param>
void SetIsSensitiveProperty(string propertyTypeAlias, bool value);
}
}
+13 -1
View File
@@ -168,7 +168,19 @@ namespace Umbraco.Core.Models
public string RawPasswordValue
{
get { return _rawPasswordValue; }
set { SetPropertyValueAndDetectChanges(value, ref _rawPasswordValue, Ps.Value.PasswordSelector); }
set
{
if (value == null)
{
//special case, this is used to ensure that the password is not updated when persisting, in this case
//we don't want to track changes either
_rawPasswordValue = null;
}
else
{
SetPropertyValueAndDetectChanges(value, ref _rawPasswordValue, Ps.Value.PasswordSelector);
}
}
}
/// <summary>
+36 -9
View File
@@ -79,7 +79,7 @@ namespace Umbraco.Core.Models
}
/// <summary>
/// Gets or Sets a Dictionary of Tuples (MemberCanEdit, VisibleOnProfile) by the PropertyTypes' alias.
/// Gets or Sets a Dictionary of Tuples (MemberCanEdit, VisibleOnProfile, IsSensitive) by the PropertyTypes' alias.
/// </summary>
[DataMember]
internal IDictionary<string, MemberTypePropertyProfileAccess> MemberTypePropertyTypes { get; private set; }
@@ -91,7 +91,7 @@ namespace Umbraco.Core.Models
/// <returns></returns>
public bool MemberCanEditProperty(string propertyTypeAlias)
{
return MemberTypePropertyTypes.ContainsKey(propertyTypeAlias) && MemberTypePropertyTypes[propertyTypeAlias].IsEditable;
return MemberTypePropertyTypes.TryGetValue(propertyTypeAlias, out var propertyProfile) && propertyProfile.IsEditable;
}
/// <summary>
@@ -101,7 +101,16 @@ namespace Umbraco.Core.Models
/// <returns></returns>
public bool MemberCanViewProperty(string propertyTypeAlias)
{
return MemberTypePropertyTypes.ContainsKey(propertyTypeAlias) && MemberTypePropertyTypes[propertyTypeAlias].IsVisible;
return MemberTypePropertyTypes.TryGetValue(propertyTypeAlias, out var propertyProfile) && propertyProfile.IsVisible;
}
/// <summary>
/// Gets a boolean indicating whether a Property is marked as storing sensitive values on the Members profile.
/// </summary>
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to check</param>
/// <returns></returns>
public bool IsSensitiveProperty(string propertyTypeAlias)
{
return MemberTypePropertyTypes.TryGetValue(propertyTypeAlias, out var propertyProfile) && propertyProfile.IsSensitive;
}
/// <summary>
@@ -111,13 +120,13 @@ namespace Umbraco.Core.Models
/// <param name="value">Boolean value, true or false</param>
public void SetMemberCanEditProperty(string propertyTypeAlias, bool value)
{
if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias))
if (MemberTypePropertyTypes.TryGetValue(propertyTypeAlias, out var propertyProfile))
{
MemberTypePropertyTypes[propertyTypeAlias].IsEditable = value;
propertyProfile.IsEditable = value;
}
else
{
var tuple = new MemberTypePropertyProfileAccess(false, value);
var tuple = new MemberTypePropertyProfileAccess(false, value, false);
MemberTypePropertyTypes.Add(propertyTypeAlias, tuple);
}
}
@@ -129,13 +138,31 @@ namespace Umbraco.Core.Models
/// <param name="value">Boolean value, true or false</param>
public void SetMemberCanViewProperty(string propertyTypeAlias, bool value)
{
if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias))
if (MemberTypePropertyTypes.TryGetValue(propertyTypeAlias, out var propertyProfile))
{
MemberTypePropertyTypes[propertyTypeAlias].IsVisible = value;
propertyProfile.IsVisible = value;
}
else
{
var tuple = new MemberTypePropertyProfileAccess(value, false);
var tuple = new MemberTypePropertyProfileAccess(value, false, false);
MemberTypePropertyTypes.Add(propertyTypeAlias, tuple);
}
}
/// <summary>
/// Sets a boolean indicating whether a Property is a sensitive value on the Members profile.
/// </summary>
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to set</param>
/// <param name="value">Boolean value, true or false</param>
public void SetIsSensitiveProperty(string propertyTypeAlias, bool value)
{
if (MemberTypePropertyTypes.TryGetValue(propertyTypeAlias, out var propertyProfile))
{
propertyProfile.IsSensitive = value;
}
else
{
var tuple = new MemberTypePropertyProfileAccess(false, false, true);
MemberTypePropertyTypes.Add(propertyTypeAlias, tuple);
}
}
@@ -5,13 +5,15 @@
/// </summary>
internal class MemberTypePropertyProfileAccess
{
public MemberTypePropertyProfileAccess(bool isVisible, bool isEditable)
public MemberTypePropertyProfileAccess(bool isVisible, bool isEditable, bool isSenstive)
{
IsVisible = isVisible;
IsEditable = isEditable;
IsSensitive = isSenstive;
}
public bool IsVisible { get; set; }
public bool IsEditable { get; set; }
public bool IsSensitive { get; set; }
}
}
@@ -188,11 +188,11 @@ namespace Umbraco.Core.Models
Language,
/// <summary>
/// Document
/// Document Blueprint
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.Strings.DocumentBlueprint, typeof(IContent))]
[FriendlyName("DocumentBlueprint")]
[UmbracoUdiType(Constants.UdiEntityType.DocumentBluePrint)]
[UmbracoUdiType(Constants.UdiEntityType.DocumentBlueprint)]
DocumentBlueprint,
/// <summary>
+11 -2
View File
@@ -48,12 +48,11 @@ namespace Umbraco.Core.Models
/// Tries to lookup the user's gravatar to see if the endpoint can be reached, if so it returns the valid URL
/// </summary>
/// <param name="user"></param>
/// <param name="userService"></param>
/// <param name="staticCache"></param>
/// <returns>
/// A list of 5 different sized avatar URLs
/// </returns>
internal static string[] GetCurrentUserAvatarUrls(this IUser user, IUserService userService, ICacheProvider staticCache)
internal static string[] GetUserAvatarUrls(this IUser user, ICacheProvider staticCache)
{
//check if the user has explicitly removed all avatars including a gravatar, this will be possible and the value will be "none"
if (user.Avatar == "none")
@@ -276,6 +275,16 @@ namespace Umbraco.Core.Models
return false;
}
/// <summary>
/// Determines whether this user has access to view sensitive data
/// </summary>
/// <param name="user"></param>
public static bool HasAccessToSensitiveData(this IUser user)
{
if (user == null) throw new ArgumentNullException("user");
return user.Groups != null && user.Groups.Any(x => x.Alias == Constants.Security.SensitiveDataGroupAlias);
}
// calc. start nodes, combining groups' and user's, and excluding what's in the bin
public static int[] CalculateContentStartNodeIds(this IUser user, IEntityService entityService)
{
@@ -28,6 +28,7 @@ namespace Umbraco.Core
public const string ContentVersion = TableNamePrefix + "ContentVersion";
public const string Document = TableNamePrefix + "Document";
public const string DocumentVersion = TableNamePrefix + "DocumentVersion";
public const string MediaVersion = TableNamePrefix + "MediaVersion";
public const string PropertyType = /*TableNamePrefix*/ "cms" + "PropertyType";
public const string PropertyTypeGroup = /*TableNamePrefix*/ "cms" + "PropertyTypeGroup";
@@ -0,0 +1,21 @@
using NPoco;
namespace Umbraco.Core.Persistence.Dtos
{
// this is a special Dto that does not have a corresponding table
// and is only used in our code to represent a media item, similar
// to document items.
internal class MediaDto
{
public int NodeId { get; set; }
[ResultColumn]
[Reference(ReferenceType.OneToOne, ReferenceMemberName = "NodeId")]
public ContentDto ContentDto { get; set; }
[ResultColumn]
[Reference(ReferenceType.OneToOne)]
public MediaVersionDto MediaVersionDto { get; set; }
}
}
@@ -0,0 +1,25 @@
using NPoco;
using Umbraco.Core.Persistence.DatabaseAnnotations;
namespace Umbraco.Core.Persistence.Dtos
{
[TableName(Constants.DatabaseSchema.Tables.MediaVersion)]
[PrimaryKey("id", AutoIncrement = false)]
[ExplicitColumns]
internal class MediaVersionDto
{
[Column("id")]
[PrimaryKeyColumn(AutoIncrement = false)]
[ForeignKey(typeof(ContentVersionDto))]
[Index(IndexTypes.UniqueNonClustered, Name = "IX_" + Constants.DatabaseSchema.Tables.MediaVersion, ForColumns = "id, path")]
public int Id { get; set; }
[Column("path")]
[NullSetting(NullSetting = NullSettings.Null)]
public string Path { get; set; }
[ResultColumn]
[Reference(ReferenceType.OneToOne)]
public ContentVersionDto ContentVersionDto { get; set; }
}
}
@@ -0,0 +1,52 @@
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Persistence.Factories
{
internal static class AuditEntryFactory
{
public static IEnumerable<IAuditEntry> BuildEntities(IEnumerable<AuditEntryDto> dtos)
{
return dtos.Select(BuildEntity).ToList();
}
public static IAuditEntry BuildEntity(AuditEntryDto dto)
{
var entity = new AuditEntry
{
Id = dto.Id,
PerformingUserId = dto.PerformingUserId,
PerformingDetails = dto.PerformingDetails,
PerformingIp = dto.PerformingIp,
EventDateUtc = dto.EventDateUtc,
AffectedUserId = dto.AffectedUserId,
AffectedDetails = dto.AffectedDetails,
EventType = dto.EventType,
EventDetails = dto.EventDetails
};
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
entity.ResetDirtyProperties(false);
return entity;
}
public static AuditEntryDto BuildDto(IAuditEntry entity)
{
return new AuditEntryDto
{
Id = entity.Id,
PerformingUserId = entity.PerformingUserId,
PerformingDetails = entity.PerformingDetails,
PerformingIp = entity.PerformingIp,
EventDateUtc = entity.EventDateUtc,
AffectedUserId = entity.AffectedUserId,
AffectedDetails = entity.AffectedDetails,
EventType = entity.EventType,
EventDetails = entity.EventDetails
};
}
}
}
@@ -0,0 +1,65 @@
using System.Collections.Generic;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Persistence.Factories
{
internal static class ConsentFactory
{
public static IEnumerable<IConsent> BuildEntities(IEnumerable<ConsentDto> dtos)
{
var ix = new Dictionary<string, Consent>();
var output = new List<Consent>();
foreach (var dto in dtos)
{
var k = dto.Source + "::" + dto.Context + "::" + dto.Action;
var consent = new Consent
{
Id = dto.Id,
Current = dto.Current,
CreateDate = dto.CreateDate,
Source = dto.Source,
Context = dto.Context,
Action = dto.Action,
State = (ConsentState) dto.State, // assume value is valid
Comment = dto.Comment
};
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
consent.ResetDirtyProperties(false);
if (ix.TryGetValue(k, out var current))
{
if (current.HistoryInternal == null)
current.HistoryInternal = new List<IConsent>();
current.HistoryInternal.Add(consent);
}
else
{
ix[k] = consent;
output.Add(consent);
}
}
return output;
}
public static ConsentDto BuildDto(IConsent entity)
{
return new ConsentDto
{
Id = entity.Id,
Current = entity.Current,
CreateDate = entity.CreateDate,
Source = entity.Source,
Context = entity.Context,
Action = entity.Action,
State = (int) entity.State,
Comment = entity.Comment
};
}
}
}
@@ -1,4 +1,5 @@
using System;
using System.Text.RegularExpressions;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
@@ -6,6 +7,8 @@ namespace Umbraco.Core.Persistence.Factories
{
internal class ContentBaseFactory
{
private static readonly Regex MediaPathPattern = new Regex(@"(/media/.+?)(?:['""]|$)", RegexOptions.Compiled);
/// <summary>
/// Builds an IContent item from a dto and content type.
/// </summary>
@@ -177,11 +180,18 @@ namespace Umbraco.Core.Persistence.Factories
/// <summary>
/// Buils a dto from an IMedia item.
/// </summary>
public static ContentDto BuildDto(IMedia entity)
public static MediaDto BuildDto(IMedia entity)
{
var contentBase = (Models.Media) entity;
var dto = BuildContentDto(contentBase, Constants.ObjectTypes.Media);
dto.ContentVersionDto = BuildContentVersionDto(contentBase, dto);
var contentDto = BuildContentDto(contentBase, Constants.ObjectTypes.Media);
var dto = new MediaDto
{
NodeId = entity.Id,
ContentDto = contentDto,
MediaVersionDto = BuildMediaVersionDto(contentBase, contentDto)
};
return dto;
}
@@ -273,5 +283,37 @@ namespace Umbraco.Core.Persistence.Factories
return dto;
}
private static MediaVersionDto BuildMediaVersionDto(Models.Media entity, ContentDto contentDto)
{
// try to get a path from the string being stored for media
// fixme - only considering umbracoFile ?!
TryMatch(entity.GetValue<string>("umbracoFile"), out var path);
var dto = new MediaVersionDto
{
Id = entity.VersionId,
Path = path,
ContentVersionDto = BuildContentVersionDto(entity, contentDto)
};
return dto;
}
// fixme - this should NOT be here?!
// more dark magic ;-(
internal static bool TryMatch(string text, out string path)
{
path = null;
if (string.IsNullOrWhiteSpace(text)) return false;
var m = MediaPathPattern.Match(text);
if (!m.Success || m.Groups.Count != 2) return false;
path = m.Groups[1].Value;
return true;
}
}
}
@@ -82,7 +82,8 @@ namespace Umbraco.Core.Persistence.Factories
NodeId = entity.Id,
PropertyTypeId = x.Id,
CanEdit = memberType.MemberCanEditProperty(x.Alias),
ViewOnProfile = memberType.MemberCanViewProperty(x.Alias)
ViewOnProfile = memberType.MemberCanViewProperty(x.Alias),
IsSensitive = memberType.IsSensitiveProperty(x.Alias)
}).ToList();
return dtos;
}
@@ -57,10 +57,10 @@ namespace Umbraco.Core.Persistence.Factories
//Add the standard PropertyType to the current list
propertyTypes.Add(standardPropertyType.Value);
//Internal dictionary for adding "MemberCanEdit" and "VisibleOnProfile" properties to each PropertyType
//Internal dictionary for adding "MemberCanEdit", "VisibleOnProfile", "IsSensitive" properties to each PropertyType
memberType.MemberTypePropertyTypes.Add(standardPropertyType.Key,
new MemberTypePropertyProfileAccess(false, false));
new MemberTypePropertyProfileAccess(false, false, false));
}
memberType.NoGroupPropertyTypes = propertyTypes;
@@ -103,7 +103,7 @@ namespace Umbraco.Core.Persistence.Factories
{
//Internal dictionary for adding "MemberCanEdit" and "VisibleOnProfile" properties to each PropertyType
memberType.MemberTypePropertyTypes.Add(typeDto.Alias,
new MemberTypePropertyProfileAccess(typeDto.ViewOnProfile, typeDto.CanEdit));
new MemberTypePropertyProfileAccess(typeDto.ViewOnProfile, typeDto.CanEdit, typeDto.IsSensitive));
var tempGroupDto = groupDto;
@@ -158,7 +158,7 @@ namespace Umbraco.Core.Persistence.Factories
{
//Internal dictionary for adding "MemberCanEdit" and "VisibleOnProfile" properties to each PropertyType
memberType.MemberTypePropertyTypes.Add(typeDto.Alias,
new MemberTypePropertyProfileAccess(typeDto.ViewOnProfile, typeDto.CanEdit));
new MemberTypePropertyProfileAccess(typeDto.ViewOnProfile, typeDto.CanEdit, typeDto.IsSensitive));
//ensures that any built-in membership properties have their correct dbtype assigned no matter
//what the underlying data type is
@@ -36,6 +36,7 @@ namespace Umbraco.Core.Persistence.Factories
user.Avatar = dto.Avatar;
user.EmailConfirmedDate = dto.EmailConfirmedDate;
user.InvitedDate = dto.InvitedDate;
user.TourData = dto.TourData;
// reset dirty initial properties (U4-1946)
user.ResetDirtyProperties(false);
@@ -68,7 +69,8 @@ namespace Umbraco.Core.Persistence.Factories
UpdateDate = entity.UpdateDate,
Avatar = entity.Avatar,
EmailConfirmedDate = entity.EmailConfirmedDate,
InvitedDate = entity.InvitedDate
InvitedDate = entity.InvitedDate,
TourData = entity.TourData
};
foreach (var startNodeId in entity.StartContentIds)
@@ -0,0 +1,40 @@
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Persistence.Mappers
{
/// <summary>
/// Represents a mapper for audit entry entities.
/// </summary>
[MapperFor(typeof(IAuditEntry))]
[MapperFor(typeof(AuditEntry))]
public sealed class AuditEntryMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
/// <summary>
/// Initializes a new instance of the <see cref="AuditEntryMapper"/> class.
/// </summary>
public AuditEntryMapper()
{
// note: why the base ctor does not invoke BuildMap is a mystery to me
BuildMap();
}
protected override void BuildMap()
{
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.Id, dto => dto.Id);
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.PerformingUserId, dto => dto.PerformingUserId);
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.PerformingDetails, dto => dto.PerformingDetails);
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.PerformingIp, dto => dto.PerformingIp);
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.EventDateUtc, dto => dto.EventDateUtc);
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.AffectedUserId, dto => dto.AffectedUserId);
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.AffectedDetails, dto => dto.AffectedDetails);
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.EventType, dto => dto.EventType);
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.EventDetails, dto => dto.EventDetails);
}
}
}
@@ -0,0 +1,32 @@
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Persistence.Mappers
{
[MapperFor(typeof(AuditItem))]
[MapperFor(typeof(IAuditItem))]
public sealed class AuditMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
public AuditMapper()
{
BuildMap();
}
protected override void BuildMap()
{
if (PropertyInfoCache.IsEmpty)
{
CacheMap<AuditItem, LogDto>(src => src.Id, dto => dto.NodeId);
CacheMap<AuditItem, LogDto>(src => src.CreateDate, dto => dto.Datestamp);
CacheMap<AuditItem, LogDto>(src => src.UserId, dto => dto.UserId);
CacheMap<AuditItem, LogDto>(src => src.AuditType, dto => dto.Header);
CacheMap<AuditItem, LogDto>(src => src.Comment, dto => dto.Comment);
}
}
}
}
@@ -0,0 +1,39 @@
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Persistence.Mappers
{
/// <summary>
/// Represents a mapper for consent entities.
/// </summary>
[MapperFor(typeof(IConsent))]
[MapperFor(typeof(Consent))]
public sealed class ConsentMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
/// <summary>
/// Initializes a new instance of the <see cref="ConsentMapper"/> class.
/// </summary>
public ConsentMapper()
{
// note: why the base ctor does not invoke BuildMap is a mystery to me
BuildMap();
}
protected override void BuildMap()
{
CacheMap<Consent, ConsentDto>(entity => entity.Id, dto => dto.Id);
CacheMap<Consent, ConsentDto>(entity => entity.Current, dto => dto.Current);
CacheMap<Consent, ConsentDto>(entity => entity.CreateDate, dto => dto.CreateDate);
CacheMap<Consent, ConsentDto>(entity => entity.Source, dto => dto.Source);
CacheMap<Consent, ConsentDto>(entity => entity.Context, dto => dto.Context);
CacheMap<Consent, ConsentDto>(entity => entity.Action, dto => dto.Action);
CacheMap<Consent, ConsentDto>(entity => entity.State, dto => dto.State);
CacheMap<Consent, ConsentDto>(entity => entity.Comment, dto => dto.Comment);
}
}
}
+23
View File
@@ -326,10 +326,23 @@
<Compile Include="KeyValuePairExtensions.cs" />
<Compile Include="Logging\RollingFileCleanupAppender.cs" />
<Compile Include="Migrations\MigrationBase_Extra.cs" />
<Compile Include="Migrations\Upgrade\V_7_8_0\AddIndexToPropertyTypeAliasColumn.cs" />
<Compile Include="Migrations\Upgrade\V_7_8_0\AddInstructionCountColumn.cs" />
<Compile Include="Migrations\Upgrade\V_7_8_0\AddMediaVersionTable.cs" />
<Compile Include="Migrations\Upgrade\V_7_8_0\AddTourDataUserColumn.cs" />
<Compile Include="Migrations\Upgrade\V_7_8_0\AddUserLoginTable.cs" />
<Compile Include="Migrations\Upgrade\V_7_9_0\AddIsSensitiveMemberTypeColumn.cs" />
<Compile Include="Migrations\Upgrade\V_7_9_0\AddUmbracoAuditTable.cs" />
<Compile Include="Migrations\Upgrade\V_7_9_0\AddUmbracoConsentTable.cs" />
<Compile Include="Migrations\Upgrade\V_7_9_0\CreateSensitiveDataUserGroup.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\DataTypeMigration.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\PropertyEditorsMigration.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\SuperZero.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\TagsMigration.cs" />
<Compile Include="Models\AuditEntry.cs" />
<Compile Include="Models\Consent.cs" />
<Compile Include="Models\ConsentExtensions.cs" />
<Compile Include="Models\ConsentState.cs" />
<Compile Include="Models\ContentTagsExtensions.cs" />
<Compile Include="Models\ContentTypeBaseExtensions.cs" />
<Compile Include="Models\DataTypeExtensions.cs" />
@@ -342,6 +355,9 @@
<Compile Include="Models\Entities\IDocumentEntitySlim.cs" />
<Compile Include="Models\Entities\ContentEntitySlim.cs" />
<Compile Include="Models\Entities\DocumentEntitySlim.cs" />
<Compile Include="Models\IAuditEntry.cs" />
<Compile Include="Models\IAuditItem.cs" />
<Compile Include="Models\IConsent.cs" />
<Compile Include="Models\Membership\MemberExportModel.cs" />
<Compile Include="Models\Membership\MemberExportProperty.cs" />
<Compile Include="Models\PathValidationExtensions.cs" />
@@ -349,7 +365,14 @@
<Compile Include="Models\PropertyTagsExtensions.cs" />
<Compile Include="Persistence\Dtos\AuditEntryDto.cs" />
<Compile Include="Persistence\Dtos\ConsentDto.cs" />
<Compile Include="Persistence\Dtos\MediaDto.cs" />
<Compile Include="Persistence\Dtos\MediaVersionDto.cs" />
<Compile Include="Persistence\Dtos\UserLoginDto.cs" />
<Compile Include="Persistence\Factories\AuditEntryFactory.cs" />
<Compile Include="Persistence\Factories\ConsentFactory.cs" />
<Compile Include="Persistence\Mappers\AuditEntryMapper.cs" />
<Compile Include="Persistence\Mappers\AuditMapper.cs" />
<Compile Include="Persistence\Mappers\ConsentMapper.cs" />
<Compile Include="PropertyEditors\ConfigurationEditorOfTConfiguration.cs" />
<Compile Include="PropertyEditors\DataEditorAttribute.cs" />
<Compile Include="PropertyEditors\ConfigurationEditor.cs" />
@@ -240,9 +240,9 @@ namespace Umbraco.Tests.Migrations
foreach (var x in DatabaseSchemaCreator.OrderedTables)
{
// ok - for tests, restrict to Node
if (x.Value != typeof(NodeDto)) continue;
if (x != typeof(NodeDto)) continue;
Create.KeysAndIndexes(x.Value).Do();
Create.KeysAndIndexes(x).Do();
}
}
}