Port v7@2aa0dfb2c5 - WIP

This commit is contained in:
Stephan
2018-03-22 11:24:12 +01:00
parent 8bfb8e2b72
commit 62c5f0f1ef
17 changed files with 590 additions and 148 deletions

View File

@@ -24,6 +24,7 @@ namespace Umbraco.Core.Persistence
{
private int _version;
private bool _hasVersion;
private string _exe;
#region Availability & Version
@@ -84,16 +85,31 @@ namespace Umbraco.Core.Persistence
{
_hasVersion = true;
_version = -1;
_exe = null;
var programFiles = Environment.GetEnvironmentVariable("ProgramFiles");
if (programFiles == null) return;
// MS SQL Server installs in e.g. "C:\Program Files\Microsoft SQL Server", so
// we want to detect it in "%ProgramFiles%\Microsoft SQL Server" - however, if
// Umbraco runs as a 32bits process (e.g. IISExpress configured as 32bits)
// on a 64bits system, %ProgramFiles% will point to "C:\Program Files (x86)"
// and SQL Server cannot be found. But then, %ProgramW6432% will point to
// the original "C:\Program Files". Using it to fix the path.
// see also: MSDN doc for WOW64 implementation
//
var programW6432 = Environment.GetEnvironmentVariable("ProgramW6432");
if (string.IsNullOrWhiteSpace(programW6432) == false && programW6432 != programFiles)
programFiles = programW6432;
if (string.IsNullOrWhiteSpace(programFiles)) return;
// detect 14, 13, 12, 11
for (var i = 14; i > 10; i--)
{
var path = Path.Combine(programFiles, string.Format(@"Microsoft SQL Server\{0}0\Tools\Binn\SqlLocalDB.exe", i));
if (File.Exists(path) == false) continue;
var exe = Path.Combine(programFiles, $@"Microsoft SQL Server\{i}0\Tools\Binn\SqlLocalDB.exe");
if (File.Exists(exe) == false) continue;
_version = i;
_exe = exe;
break;
}
}
@@ -110,8 +126,7 @@ namespace Umbraco.Core.Persistence
public string[] GetInstances()
{
EnsureAvailable();
string output, error;
var rc = ExecuteSqlLocalDb("i", out output, out error); // info
var rc = ExecuteSqlLocalDb("i", out var output, out var error); // info
if (rc != 0 || error != string.Empty) return null;
return output.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
}
@@ -138,8 +153,7 @@ namespace Umbraco.Core.Persistence
public bool CreateInstance(string instanceName)
{
EnsureAvailable();
string output, error;
return ExecuteSqlLocalDb(string.Format("c \"{0}\"", instanceName), out output, out error) == 0 && error == string.Empty;
return ExecuteSqlLocalDb($"c \"{instanceName}\"", out _, out var error) == 0 && error == string.Empty;
}
/// <summary>
@@ -160,9 +174,8 @@ namespace Umbraco.Core.Persistence
instance.DropDatabases(); // else the files remain
// -i force NOWAIT, -k kills
string output, error;
return ExecuteSqlLocalDb(string.Format("p \"{0}\" -i", instanceName), out output, out error) == 0 && error == string.Empty
&& ExecuteSqlLocalDb(string.Format("d \"{0}\"", instanceName), out output, out error) == 0 && error == string.Empty;
return ExecuteSqlLocalDb($"p \"{instanceName}\" -i", out _, out var error) == 0 && error == string.Empty
&& ExecuteSqlLocalDb($"d \"{instanceName}\"", out _, out error) == 0 && error == string.Empty;
}
/// <summary>
@@ -180,8 +193,7 @@ namespace Umbraco.Core.Persistence
if (InstanceExists(instanceName) == false) return true;
// -i force NOWAIT, -k kills
string output, error;
return ExecuteSqlLocalDb(string.Format("p \"{0}\" -i", instanceName), out output, out error) == 0 && error == string.Empty;
return ExecuteSqlLocalDb($"p \"{instanceName}\" -i", out _, out var error) == 0 && error == string.Empty;
}
/// <summary>
@@ -197,8 +209,7 @@ namespace Umbraco.Core.Persistence
{
EnsureAvailable();
if (InstanceExists(instanceName) == false) return false;
string output, error;
return ExecuteSqlLocalDb(string.Format("s \"{0}\"", instanceName), out output, out error) == 0 && error == string.Empty;
return ExecuteSqlLocalDb($"s \"{instanceName}\"", out _, out var error) == 0 && error == string.Empty;
}
/// <summary>
@@ -230,7 +241,7 @@ namespace Umbraco.Core.Persistence
/// <summary>
/// Gets the name of the instance.
/// </summary>
public string InstanceName { get; private set; }
public string InstanceName { get; }
/// <summary>
/// Initializes a new instance of the <see cref="Instance"/> class.
@@ -239,7 +250,7 @@ namespace Umbraco.Core.Persistence
public Instance(string instanceName)
{
InstanceName = instanceName;
_masterCstr = string.Format(@"Server=(localdb)\{0};Integrated Security=True;", instanceName);
_masterCstr = $@"Server=(localdb)\{instanceName};Integrated Security=True;";
}
/// <summary>
@@ -252,7 +263,7 @@ namespace Umbraco.Core.Persistence
/// </remarks>
public string GetConnectionString(string databaseName)
{
return _masterCstr + string.Format(@"Database={0};", databaseName);
return _masterCstr + $@"Database={databaseName};";
}
/// <summary>
@@ -268,10 +279,9 @@ namespace Umbraco.Core.Persistence
/// </remarks>
public string GetAttachedConnectionString(string databaseName, string filesPath)
{
string logName, baseFilename, baseLogFilename, mdfFilename, ldfFilename;
GetDatabaseFiles(databaseName, filesPath, out logName, out baseFilename, out baseLogFilename, out mdfFilename, out ldfFilename);
GetDatabaseFiles(databaseName, filesPath, out _, out _, out _, out var mdfFilename, out _);
return _masterCstr + string.Format(@"AttachDbFileName='{0}';", mdfFilename);
return _masterCstr + $@"AttachDbFileName='{mdfFilename}';";
}
/// <summary>
@@ -367,8 +377,7 @@ namespace Umbraco.Core.Persistence
/// </remarks>
public bool CreateDatabase(string databaseName, string filesPath)
{
string logName, baseFilename, baseLogFilename, mdfFilename, ldfFilename;
GetDatabaseFiles(databaseName, filesPath, out logName, out baseFilename, out baseLogFilename, out mdfFilename, out ldfFilename);
GetDatabaseFiles(databaseName, filesPath, out var logName, out _, out _, out var mdfFilename, out var ldfFilename);
using (var conn = new SqlConnection(_masterCstr))
using (var cmd = conn.CreateCommand())
@@ -380,13 +389,10 @@ namespace Umbraco.Core.Persistence
// cannot use parameters on CREATE DATABASE
// ie "CREATE DATABASE @0 ..." does not work
SetCommand(cmd, string.Format(@"
CREATE DATABASE {0}
ON (NAME=N{1}, FILENAME={2})
LOG ON (NAME=N{3}, FILENAME={4})",
QuotedName(databaseName),
QuotedName(databaseName, '\''), QuotedName(mdfFilename, '\''),
QuotedName(logName, '\''), QuotedName(ldfFilename, '\'')));
SetCommand(cmd, $@"
CREATE DATABASE {QuotedName(databaseName)}
ON (NAME=N{QuotedName(databaseName, '\'')}, FILENAME={QuotedName(mdfFilename, '\'')})
LOG ON (NAME=N{QuotedName(logName, '\'')}, FILENAME={QuotedName(ldfFilename, '\'')})");
var unused = cmd.ExecuteNonQuery();
}
@@ -608,9 +614,8 @@ namespace Umbraco.Core.Persistence
{
// cannot use parameters on ALTER DATABASE
// ie "ALTER DATABASE @0 ..." does not work
SetCommand(cmd, string.Format(@"
ALTER DATABASE {0} SET SINGLE_USER WITH ROLLBACK IMMEDIATE",
QuotedName(databaseName)));
SetCommand(cmd, $@"
ALTER DATABASE {QuotedName(databaseName)} SET SINGLE_USER WITH ROLLBACK IMMEDIATE");
var unused1 = cmd.ExecuteNonQuery();
}
@@ -631,9 +636,8 @@ namespace Umbraco.Core.Persistence
// cannot use parameters on DROP DATABASE
// ie "DROP DATABASE @0 ..." does not work
SetCommand(cmd, string.Format(@"
DROP DATABASE {0}",
QuotedName(databaseName)));
SetCommand(cmd, $@"
DROP DATABASE {QuotedName(databaseName)}");
var unused2 = cmd.ExecuteNonQuery();
@@ -651,7 +655,7 @@ namespace Umbraco.Core.Persistence
private static string GetLogFilename(string mdfFilename)
{
if (mdfFilename.EndsWith(".mdf") == false)
throw new ArgumentException("Not a valid MDF filename (no .mdf extension).", "mdfFilename");
throw new ArgumentException("Not a valid MDF filename (no .mdf extension).", nameof(mdfFilename));
return mdfFilename.Substring(0, mdfFilename.Length - ".mdf".Length) + "_log.ldf";
}
@@ -664,9 +668,8 @@ namespace Umbraco.Core.Persistence
{
// cannot use parameters on ALTER DATABASE
// ie "ALTER DATABASE @0 ..." does not work
SetCommand(cmd, string.Format(@"
ALTER DATABASE {0} SET SINGLE_USER WITH ROLLBACK IMMEDIATE",
QuotedName(databaseName)));
SetCommand(cmd, $@"
ALTER DATABASE {QuotedName(databaseName)} SET SINGLE_USER WITH ROLLBACK IMMEDIATE");
var unused1 = cmd.ExecuteNonQuery();
@@ -685,19 +688,16 @@ namespace Umbraco.Core.Persistence
/// <param name="filesPath">The directory containing database files.</param>
private static void AttachDatabase(SqlCommand cmd, string databaseName, string filesPath)
{
string logName, baseFilename, baseLogFilename, mdfFilename, ldfFilename;
GetDatabaseFiles(databaseName, filesPath, out logName, out baseFilename, out baseLogFilename, out mdfFilename, out ldfFilename);
GetDatabaseFiles(databaseName, filesPath,
out var logName, out _, out _, out var mdfFilename, out var ldfFilename);
// cannot use parameters on CREATE DATABASE
// ie "CREATE DATABASE @0 ..." does not work
SetCommand(cmd, string.Format(@"
CREATE DATABASE {0}
ON (NAME=N{1}, FILENAME={2})
LOG ON (NAME=N{3}, FILENAME={4})
FOR ATTACH",
QuotedName(databaseName),
QuotedName(databaseName, '\''), QuotedName(mdfFilename, '\''),
QuotedName(logName, '\''), QuotedName(ldfFilename, '\'')));
SetCommand(cmd, $@"
CREATE DATABASE {QuotedName(databaseName)}
ON (NAME=N{QuotedName(databaseName, '\'')}, FILENAME={QuotedName(mdfFilename, '\'')})
LOG ON (NAME=N{QuotedName(logName, '\'')}, FILENAME={QuotedName(ldfFilename, '\'')})
FOR ATTACH");
var unused = cmd.ExecuteNonQuery();
}
@@ -790,9 +790,8 @@ namespace Umbraco.Core.Persistence
&& (sourceExtension == null && targetExtension == null || sourceExtension == targetExtension);
if (nop && delete == false) return;
string logName, baseFilename, baseLogFilename, mdfFilename, ldfFilename;
GetDatabaseFiles(databaseName, filesPath,
out logName, out baseFilename, out baseLogFilename, out mdfFilename, out ldfFilename);
out _, out _, out _, out var mdfFilename, out var ldfFilename);
if (sourceExtension != null)
{
@@ -809,9 +808,8 @@ namespace Umbraco.Core.Persistence
else
{
// copy or copy+delete ie move
string targetLogName, targetBaseFilename, targetLogFilename, targetMdfFilename, targetLdfFilename;
GetDatabaseFiles(targetDatabaseName ?? databaseName, targetFilesPath ?? filesPath,
out targetLogName, out targetBaseFilename, out targetLogFilename, out targetMdfFilename, out targetLdfFilename);
out _, out _, out _, out var targetMdfFilename, out var targetLdfFilename);
if (targetExtension != null)
{
@@ -846,9 +844,8 @@ namespace Umbraco.Core.Persistence
/// </remarks>
public bool DatabaseFilesExist(string databaseName, string filesPath, string extension = null)
{
string logName, baseFilename, baseLogFilename, mdfFilename, ldfFilename;
GetDatabaseFiles(databaseName, filesPath,
out logName, out baseFilename, out baseLogFilename, out mdfFilename, out ldfFilename);
out _, out _, out _, out var mdfFilename, out var ldfFilename);
if (extension != null)
{
@@ -897,16 +894,13 @@ namespace Umbraco.Core.Persistence
/// </remarks>
private int ExecuteSqlLocalDb(string args, out string output, out string error)
{
var programFiles = Environment.GetEnvironmentVariable("ProgramFiles");
if (programFiles == null)
if (_exe == null) // should never happen - we should not execute if not available
{
output = string.Empty;
error = "SqlLocalDB.exe not found";
return -1;
}
var path = Path.Combine(programFiles, string.Format(@"Microsoft SQL Server\{0}0\Tools\Binn\SqlLocalDB.exe", _version));
var p = new Process
{
StartInfo =
@@ -914,7 +908,7 @@ namespace Umbraco.Core.Persistence
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
FileName = path,
FileName = _exe,
Arguments = args,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden

View File

@@ -40,7 +40,12 @@ namespace Umbraco.Core.Persistence
_tableDefinition = DefinitionFactory.GetTableDefinition(pd.Type, sqlSyntaxProvider);
if (_tableDefinition == null) throw new InvalidOperationException("No table definition found for type " + pd.Type);
_readerColumns = pd.Columns.Select(x => x.Value).ToArray();
// only real columns, exclude result columns
_readerColumns = pd.Columns
.Where(x => x.Value.ResultColumn == false)
.Select(x => x.Value)
.ToArray();
_sqlSyntaxProvider = sqlSyntaxProvider;
_enumerator = dataSource.GetEnumerator();
_columnDefinitions = _tableDefinition.Columns.ToArray();

View File

@@ -0,0 +1,22 @@
using System.Collections.Generic;
using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
/// <summary>
/// Represents a repository for <see cref="IAuditEntry"/> entities.
/// </summary>
public interface IAuditEntryRepository : IReadWriteQueryRepository<int, IAuditEntry>
{
/// <summary>
/// Gets a page of entries.
/// </summary>
IEnumerable<IAuditEntry> GetPage(long pageIndex, int pageCount, out long records);
/// <summary>
/// Determines whether the repository is available.
/// </summary>
/// <remarks>During an upgrade, the repository may not be available, until the table has been created.</remarks>
bool IsAvailable();
}
}

View File

@@ -1,9 +1,37 @@
using Umbraco.Core.Models;
using System.Collections.Generic;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IAuditRepository : IReadRepository<int, AuditItem>, IWriteRepository<AuditItem>, IQueryRepository<AuditItem>
public interface IAuditRepository : IReadRepository<int, IAuditItem>, IWriteRepository<IAuditItem>, IQueryRepository<IAuditItem>
{
void CleanLogs(int maximumAgeOfLogsInMinutes);
/// <summary>
/// Return the audit items as paged result
/// </summary>
/// <param name="query">
/// The query coming from the service
/// </param>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="totalRecords"></param>
/// <param name="orderDirection"></param>
/// <param name="auditTypeFilter">
/// Since we currently do not have enum support with our expression parser, we cannot query on AuditType in the query or the custom filter
/// so we need to do that here
/// </param>
/// <param name="customFilter">
/// A user supplied custom filter
/// </param>
/// <returns></returns>
IEnumerable<IAuditItem> GetPagedResultsByQuery(
IQuery<IAuditItem> query,
long pageIndex, int pageSize, out long totalRecords,
Direction orderDirection,
AuditType[] auditTypeFilter,
IQuery<IAuditItem> customFilter);
}
}

View File

@@ -0,0 +1,15 @@
using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
/// <summary>
/// Represents a repository for <see cref="IConsent"/> entities.
/// </summary>
public interface IConsentRepository : IReadWriteQueryRepository<int, IConsent>
{
/// <summary>
/// Clears the current flag.
/// </summary>
void ClearCurrent(string source, string context, string action);
}
}

View File

@@ -39,5 +39,7 @@ namespace Umbraco.Core.Persistence.Repositories
void AssignRoles(int[] memberIds, string[] roleNames);
void DissociateRoles(int[] memberIds, string[] roleNames);
int[] GetMemberIds(string[] names);
}
}

View File

@@ -85,5 +85,11 @@ namespace Umbraco.Core.Persistence.Repositories
IProfile GetProfile(string username);
IProfile GetProfile(int id);
IDictionary<UserState, int> GetUserStates();
Guid CreateLoginSession(int userId, string requestingIpAddress, bool cleanStaleSessions = true);
bool ValidateLoginSession(int userId, Guid sessionId);
int ClearLoginSessions(int userId);
int ClearLoginSessions(TimeSpan timespan);
void ClearLoginSession(Guid sessionId);
}
}

View File

@@ -0,0 +1,137 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NPoco;
using Umbraco.Core.Cache;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.Persistence.Factories;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.Persistence.Repositories.Implement
{
/// <summary>
/// Represents the NPoco implementation of <see cref="IAuditEntryRepository"/>.
/// </summary>
internal class AuditEntryRepository : NPocoRepositoryBase<int, IAuditEntry>, IAuditEntryRepository
{
/// <summary>
/// Initializes a new instance of the <see cref="AuditEntryRepository"/> class.
/// </summary>
public AuditEntryRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
: base(scopeAccessor, cache, logger)
{ }
/// <inheritdoc />
protected override Guid NodeObjectTypeId => throw new NotSupportedException();
/// <inheritdoc />
protected override IAuditEntry PerformGet(int id)
{
var sql = Sql()
.Select<AuditEntryDto>()
.From<AuditEntryDto>()
.Where<AuditEntryDto>(x => x.Id == id);
var dto = Database.FirstOrDefault<AuditEntryDto>(sql);
return dto == null ? null : AuditEntryFactory.BuildEntity(dto);
}
/// <inheritdoc />
protected override IEnumerable<IAuditEntry> PerformGetAll(params int[] ids)
{
if (ids.Length == 0)
{
var sql = Sql()
.Select<AuditEntryDto>()
.From<AuditEntryDto>();
return Database.Fetch<AuditEntryDto>(sql).Select(AuditEntryFactory.BuildEntity);
}
var entries = new List<IAuditEntry>();
foreach (var group in ids.InGroupsOf(2000))
{
var sql = Sql()
.Select<AuditEntryDto>()
.From<AuditEntryDto>()
.WhereIn<AuditEntryDto>(x => x.Id, group);
entries.AddRange(Database.Fetch<AuditEntryDto>(sql).Select(AuditEntryFactory.BuildEntity));
}
return entries;
}
/// <inheritdoc />
protected override IEnumerable<IAuditEntry> PerformGetByQuery(IQuery<IAuditEntry> query)
{
var sqlClause = GetBaseQuery(false);
var translator = new SqlTranslator<IAuditEntry>(sqlClause, query);
var sql = translator.Translate();
return Database.Fetch<AuditEntryDto>(sql).Select(AuditEntryFactory.BuildEntity);
}
/// <inheritdoc />
protected override Sql<ISqlContext> GetBaseQuery(bool isCount)
{
var sql = Sql();
sql = isCount ? sql.SelectCount() : sql.Select<AuditEntryDto>();
sql = sql.From<AuditEntryDto>();
return sql;
}
/// <inheritdoc />
protected override string GetBaseWhereClause()
{
return $"{Constants.DatabaseSchema.Tables.AuditEntry}.id = @id";
}
/// <inheritdoc />
protected override IEnumerable<string> GetDeleteClauses()
{
throw new NotSupportedException("Audit entries cannot be deleted.");
}
/// <inheritdoc />
protected override void PersistNewItem(IAuditEntry entity)
{
((EntityBase) entity).AddingEntity();
var dto = AuditEntryFactory.BuildDto(entity);
Database.Insert(dto);
entity.Id = dto.Id;
entity.ResetDirtyProperties();
}
/// <inheritdoc />
protected override void PersistUpdatedItem(IAuditEntry entity)
{
throw new NotSupportedException("Audit entries cannot be updated.");
}
/// <inheritdoc />
public IEnumerable<IAuditEntry> GetPage(long pageIndex, int pageCount, out long records)
{
var sql = Sql()
.Select<AuditEntryDto>()
.From<AuditEntryDto>()
.OrderByDescending<AuditEntryDto>(x => x.EventDateUtc);
var page = Database.Page<AuditEntryDto>(pageIndex + 1, pageCount, sql);
records = page.TotalItems;
return page.Items.Select(AuditEntryFactory.BuildEntity);
}
/// <inheritdoc />
public bool IsAvailable()
{
var tables = SqlSyntax.GetTablesInSchema(Database).ToArray();
return tables.InvariantContains(Constants.DatabaseSchema.Tables.AuditEntry);
}
}
}

View File

@@ -7,19 +7,20 @@ using Umbraco.Core.Cache;
using Umbraco.Core.Composing.CompositionRoots;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.Persistence.Repositories.Implement
{
internal class AuditRepository : NPocoRepositoryBase<int, AuditItem>, IAuditRepository
internal class AuditRepository : NPocoRepositoryBase<int, IAuditItem>, IAuditRepository
{
public AuditRepository(IScopeAccessor scopeAccessor, [Inject(RepositoryCompositionRoot.DisabledCache)] CacheHelper cache, ILogger logger)
: base(scopeAccessor, cache, logger)
{ }
protected override void PersistNewItem(AuditItem entity)
protected override void PersistNewItem(IAuditItem entity)
{
Database.Insert(new LogDto
{
@@ -31,8 +32,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
});
}
protected override void PersistUpdatedItem(AuditItem entity)
protected override void PersistUpdatedItem(IAuditItem entity)
{
// wtf?! inserting when updating?!
Database.Insert(new LogDto
{
Comment = entity.Comment,
@@ -43,27 +45,26 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
});
}
protected override AuditItem PerformGet(int id)
protected override IAuditItem PerformGet(int id)
{
var sql = GetBaseQuery(false);
sql.Where(GetBaseWhereClause(), new { Id = id });
var dto = Database.First<LogDto>(sql);
if (dto == null)
return null;
return new AuditItem(dto.NodeId, dto.Comment, Enum<AuditType>.Parse(dto.Header), dto.UserId);
return dto == null
? null
: new AuditItem(dto.NodeId, dto.Comment, Enum<AuditType>.Parse(dto.Header), dto.UserId);
}
protected override IEnumerable<AuditItem> PerformGetAll(params int[] ids)
protected override IEnumerable<IAuditItem> PerformGetAll(params int[] ids)
{
throw new NotImplementedException();
}
protected override IEnumerable<AuditItem> PerformGetByQuery(IQuery<AuditItem> query)
protected override IEnumerable<IAuditItem> PerformGetByQuery(IQuery<IAuditItem> query)
{
var sqlClause = GetBaseQuery(false);
var translator = new SqlTranslator<AuditItem>(sqlClause, query);
var translator = new SqlTranslator<IAuditItem>(sqlClause, query);
var sql = translator.Translate();
var dtos = Database.Fetch<LogDto>(sql);
@@ -82,6 +83,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
sql
.From<LogDto>();
if (!isCount)
sql.LeftJoin<UserDto>().On<LogDto, UserDto>((left, right) => left.UserId == right.Id);
return sql;
}
@@ -108,5 +112,61 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
"delete from umbracoLog where datestamp < @oldestPermittedLogEntry and logHeader in ('open','system')",
new {oldestPermittedLogEntry = oldestPermittedLogEntry});
}
/// <summary>
/// Return the audit items as paged result
/// </summary>
/// <param name="query">
/// The query coming from the service
/// </param>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="totalRecords"></param>
/// <param name="orderDirection"></param>
/// <param name="auditTypeFilter">
/// Since we currently do not have enum support with our expression parser, we cannot query on AuditType in the query or the custom filter
/// so we need to do that here
/// </param>
/// <param name="customFilter">
/// A user supplied custom filter
/// </param>
/// <returns></returns>
public IEnumerable<IAuditItem> GetPagedResultsByQuery(IQuery<IAuditItem> query, long pageIndex, int pageSize,
out long totalRecords, Direction orderDirection,
AuditType[] auditTypeFilter,
IQuery<IAuditItem> customFilter)
{
if (auditTypeFilter == null) auditTypeFilter = Array.Empty<AuditType>();
var sql = GetBaseQuery(false);
var translator = new SqlTranslator<IAuditItem>(sql, query ?? Query<IAuditItem>());
sql = translator.Translate();
if (customFilter != null)
foreach (var filterClause in customFilter.GetWhereClauses())
sql.Where(filterClause.Item1, filterClause.Item2);
if (auditTypeFilter.Length > 0)
foreach (var type in auditTypeFilter)
sql.Where("(logHeader=@0)", type.ToString());
sql = orderDirection == Direction.Ascending
? sql.OrderBy("Datestamp")
: sql.OrderByDescending("Datestamp");
// get page
var page = Database.Page<LogDto>(pageIndex + 1, pageSize, sql);
totalRecords = page.TotalItems;
var items = page.Items.Select(
dto => new AuditItem(dto.Id, dto.Comment, Enum<AuditType>.Parse(dto.Header), dto.UserId)).ToArray();
// map the DateStamp
for (var i = 0; i < items.Length; i++)
items[i].CreateDate = page.Items[i].Datestamp;
return items;
}
}
}

View File

@@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using NPoco;
using Umbraco.Core.Cache;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.Persistence.Factories;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.Persistence.Repositories.Implement
{
/// <summary>
/// Represents the NPoco implementation of <see cref="IConsentRepository"/>.
/// </summary>
internal class ConsentRepository : NPocoRepositoryBase<int, IConsent>, IConsentRepository
{
/// <summary>
/// Initializes a new instance of the <see cref="ConsentRepository"/> class.
/// </summary>
public ConsentRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
: base(scopeAccessor, cache, logger)
{ }
/// <inheritdoc />
protected override Guid NodeObjectTypeId => throw new NotSupportedException();
/// <inheritdoc />
protected override IConsent PerformGet(int id)
{
throw new NotSupportedException();
}
/// <inheritdoc />
protected override IEnumerable<IConsent> PerformGetAll(params int[] ids)
{
throw new NotSupportedException();
}
/// <inheritdoc />
protected override IEnumerable<IConsent> PerformGetByQuery(IQuery<IConsent> query)
{
var sqlClause = Sql().Select<ConsentDto>().From<ConsentDto>();
var translator = new SqlTranslator<IConsent>(sqlClause, query);
var sql = translator.Translate().OrderByDescending<ConsentDto>(x => x.CreateDate);
return ConsentFactory.BuildEntities(Database.Fetch<ConsentDto>(sql));
}
/// <inheritdoc />
protected override Sql<ISqlContext> GetBaseQuery(bool isCount)
{
throw new NotSupportedException();
}
/// <inheritdoc />
protected override string GetBaseWhereClause()
{
throw new NotSupportedException();
}
/// <inheritdoc />
protected override IEnumerable<string> GetDeleteClauses()
{
throw new NotSupportedException();
}
/// <inheritdoc />
protected override void PersistNewItem(IConsent entity)
{
((EntityBase) entity).AddingEntity();
var dto = ConsentFactory.BuildDto(entity);
Database.Insert(dto);
entity.Id = dto.Id;
entity.ResetDirtyProperties();
}
/// <inheritdoc />
protected override void PersistUpdatedItem(IConsent entity)
{
((EntityBase) entity).UpdatingEntity();
var dto = ConsentFactory.BuildDto(entity);
Database.Update(dto);
entity.ResetDirtyProperties();
IsolatedCache.ClearCacheItem(RepositoryCacheKeys.GetKey<IConsent>(entity.Id));
}
/// <inheritdoc />
public void ClearCurrent(string source, string context, string action)
{
var sql = Sql()
.Update<ConsentDto>(u => u.Set(x => x.Current, false))
.Where<ConsentDto>(x => x.Source == source && x.Context == context && x.Action == action && x.Current);
Database.Execute(sql);
}
}
}

View File

@@ -80,10 +80,10 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
protected override Sql<ISqlContext> GetBaseQuery(QueryType queryType)
{
return GetBaseQuery(queryType, true);
return GetBaseQuery(queryType);
}
protected virtual Sql<ISqlContext> GetBaseQuery(QueryType queryType, bool current)
protected virtual Sql<ISqlContext> GetBaseQuery(QueryType queryType, bool current = true, bool joinMediaVersion = false)
{
var sql = SqlContext.Sql();
@@ -108,6 +108,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
.InnerJoin<NodeDto>().On<ContentDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.InnerJoin<ContentVersionDto>().On<ContentDto, ContentVersionDto>(left => left.NodeId, right => right.NodeId);
if (joinMediaVersion)
sql.InnerJoin<MediaVersionDto>().On<ContentVersionDto, MediaVersionDto>((left, right) => left.Id == right.Id);
sql.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
@@ -143,6 +145,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
"DELETE FROM " + Constants.DatabaseSchema.Tables.Relation + " WHERE childId = @id",
"DELETE FROM " + Constants.DatabaseSchema.Tables.TagRelationship + " WHERE nodeId = @id",
"DELETE FROM " + Constants.DatabaseSchema.Tables.Document + " WHERE nodeId = @id",
"DELETE FROM " + Constants.DatabaseSchema.Tables.MediaVersion + " WHERE id IN (SELECT id FROM " + Constants.DatabaseSchema.Tables.ContentVersion + " WHERE nodeId = @id)",
"DELETE FROM " + Constants.DatabaseSchema.Tables.PropertyData + " WHERE versionId IN (SELECT id FROM " + Constants.DatabaseSchema.Tables.ContentVersion + " WHERE nodeId = @id)",
"DELETE FROM " + Constants.DatabaseSchema.Tables.ContentVersion + " WHERE nodeId = @id",
"DELETE FROM " + Constants.DatabaseSchema.Tables.Content + " WHERE nodeId = @id",
@@ -157,7 +160,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
public override IEnumerable<IMedia> GetAllVersions(int nodeId)
{
var sql = GetBaseQuery(QueryType.Many, false)
var sql = GetBaseQuery(QueryType.Many, current: false)
.Where<NodeDto>(x => x.NodeId == nodeId)
.OrderByDescending<ContentVersionDto>(x => x.Current)
.AndByDescending<ContentVersionDto>(x => x.VersionDate);
@@ -188,29 +191,14 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
umbracoFileValue = string.Concat(mediaPath.Substring(0, underscoreIndex), mediaPath.Substring(dotIndex));
}
// If the stripped-down url returns null, we try again with the original url.
// Previously, the function would fail on e.g. "my_x_image.jpg"
var nodeId = GetMediaNodeIdByPath(Sql().Where<PropertyDataDto>(x => x.VarcharValue == umbracoFileValue));
if (nodeId < 0) nodeId = GetMediaNodeIdByPath(Sql().Where<PropertyDataDto>(x => x.VarcharValue == mediaPath));
var sql = GetBaseQuery(QueryType.Single, joinMediaVersion: true)
.Where<MediaVersionDto>(x => x.Path == umbracoFileValue)
.SelectTop(1);
// If no result so far, try getting from a json value stored in the ntext / nvarchar column
if (nodeId < 0) nodeId = GetMediaNodeIdByPath(Sql().Where("textValue LIKE @0", "%" + umbracoFileValue + "%"));
if (nodeId < 0) nodeId = GetMediaNodeIdByPath(Sql().Where("varcharValue LIKE @0", "%" + umbracoFileValue + "%"));
return nodeId < 0 ? null : Get(nodeId);
}
private int GetMediaNodeIdByPath(Sql query)
{
var sql = Sql().Select<ContentVersionDto>(x => x.NodeId)
.From<PropertyDataDto>()
.InnerJoin<PropertyTypeDto>().On<PropertyDataDto, PropertyTypeDto>(left => left.PropertyTypeId, right => right.Id)
.InnerJoin<ContentVersionDto>().On<PropertyDataDto, ContentVersionDto>((left, right) => left.VersionId == right.Id)
.Where<PropertyTypeDto>(x => x.Alias == "umbracoFile")
.Append(query);
var nodeId = Database.Fetch<int?>(sql).FirstOrDefault();
return nodeId ?? -1;
var dto = Database.Fetch<ContentDto>(sql).FirstOrDefault();
return dto == null
? null
: MapDtoToContent(dto);
}
protected override void PerformDeleteVersion(int id, int versionId)
@@ -249,7 +237,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
var sortOrder = GetNewChildSortOrder(entity.ParentId, 0);
// persist the node dto
var nodeDto = dto.NodeDto;
var nodeDto = dto.ContentDto.NodeDto;
nodeDto.Path = parent.Path;
nodeDto.Level = Convert.ToInt16(level);
nodeDto.SortOrder = sortOrder;
@@ -258,21 +246,13 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// and then either update or insert the node dto
var id = GetReservedId(nodeDto.UniqueId);
if (id > 0)
{
nodeDto.NodeId = id;
nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId);
nodeDto.ValidatePathWithException();
Database.Update(nodeDto);
}
else
{
Database.Insert(nodeDto);
// update path, now that we have an id
nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId);
nodeDto.ValidatePathWithException();
Database.Update(nodeDto);
}
nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId);
nodeDto.ValidatePathWithException();
Database.Update(nodeDto);
// update entity
entity.Id = nodeDto.NodeId;
@@ -281,17 +261,23 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
entity.Level = level;
// persist the content dto
dto.NodeId = nodeDto.NodeId;
Database.Insert(dto);
var contentDto = dto.ContentDto;
contentDto.NodeId = nodeDto.NodeId;
Database.Insert(contentDto);
// persist the content version dto
// assumes a new version id and version date (modified date) has been set
var contentVersionDto = dto.ContentVersionDto;
var contentVersionDto = dto.MediaVersionDto.ContentVersionDto;
contentVersionDto.NodeId = nodeDto.NodeId;
contentVersionDto.Current = true;
Database.Insert(contentVersionDto);
media.VersionId = contentVersionDto.Id;
// persist the media version dto
var mediaVersionDto = dto.MediaVersionDto;
mediaVersionDto.Id = media.VersionId;
Database.Insert(mediaVersionDto);
// persist the property data
var propertyDataDtos = PropertyFactory.BuildDtos(media.VersionId, 0, entity.Properties, out _);
foreach (var propertyDataDto in propertyDataDtos)
@@ -333,17 +319,19 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
var dto = ContentBaseFactory.BuildDto(entity);
// update the node dto
var nodeDto = dto.NodeDto;
var nodeDto = dto.ContentDto.NodeDto;
nodeDto.ValidatePathWithException();
Database.Update(nodeDto);
// update the content dto
Database.Update(dto);
Database.Update(dto.ContentDto);
// update the content version dto
var contentVersionDto = dto.ContentVersionDto;
// update the content & media version dtos
var contentVersionDto = dto.MediaVersionDto.ContentVersionDto;
var mediaVersionDto = dto.MediaVersionDto;
contentVersionDto.Current = true;
Database.Update(contentVersionDto);
Database.Update(mediaVersionDto);
// replace the property data
var deletePropertyDataSql = SqlContext.Sql().Delete<PropertyDataDto>().Where<PropertyDataDto>(x => x.VersionId == media.VersionId);

View File

@@ -173,20 +173,19 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
.Select("un.*")
.From("umbracoNode AS un")
.InnerJoin("cmsMember2MemberGroup")
.On("un.id = cmsMember2MemberGroup.MemberGroup")
.LeftJoin("(SELECT umbracoNode.id, cmsMember.LoginName FROM umbracoNode INNER JOIN cmsMember ON umbracoNode.id = cmsMember.nodeId) AS member")
.On("member.id = cmsMember2MemberGroup.Member")
.Where("un.nodeObjectType=@objectType", new {objectType = NodeObjectTypeId })
.Where("member.LoginName=@loginName", new {loginName = username});
.On("cmsMember2MemberGroup.MemberGroup = un.id")
.InnerJoin("cmsMember")
.On("cmsMember.nodeId = cmsMember2MemberGroup.Member")
.Where("un.nodeObjectType=@objectType", new { objectType = NodeObjectTypeId })
.Where("cmsMember.LoginName=@loginName", new { loginName = username });
return Database.Fetch<NodeDto>(sql)
.DistinctBy(dto => dto.NodeId)
.Select(x => _modelFactory.BuildEntity(x));
}
public void AssignRoles(string[] usernames, string[] roleNames)
public int[] GetMemberIds(string[] usernames)
{
//first get the member ids based on the usernames
var memberObjectType = Constants.ObjectTypes.Member;
var memberSql = Sql()
@@ -196,26 +195,17 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
.On<NodeDto, MemberDto>(dto => dto.NodeId, dto => dto.NodeId)
.Where<NodeDto>(x => x.NodeObjectType == memberObjectType)
.Where("cmsMember.LoginName in (@usernames)", new { /*usernames =*/ usernames });
var memberIds = Database.Fetch<int>(memberSql).ToArray();
return Database.Fetch<int>(memberSql).ToArray();
}
AssignRolesInternal(memberIds, roleNames);
public void AssignRoles(string[] usernames, string[] roleNames)
{
AssignRolesInternal(GetMemberIds(usernames), roleNames);
}
public void DissociateRoles(string[] usernames, string[] roleNames)
{
//first get the member ids based on the usernames
var memberObjectType = Constants.ObjectTypes.Member;
var memberSql = Sql()
.Select("umbracoNode.id")
.From<NodeDto>()
.InnerJoin<MemberDto>()
.On<NodeDto, MemberDto>(dto => dto.NodeId, dto => dto.NodeId)
.Where<NodeDto>( x => x.NodeObjectType == memberObjectType)
.Where("cmsMember.LoginName in (@usernames)", new { /*usernames =*/ usernames });
var memberIds = Database.Fetch<int>(memberSql).ToArray();
DissociateRolesInternal(memberIds, roleNames);
DissociateRolesInternal(GetMemberIds(usernames), roleNames);
}
public void AssignRoles(int[] memberIds, string[] roleNames)

View File

@@ -147,7 +147,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
.Select("umbracoNode.*", "cmsContentType.*", "cmsPropertyType.id AS PropertyTypeId", "cmsPropertyType.Alias",
"cmsPropertyType.Name", "cmsPropertyType.Description", "cmsPropertyType.mandatory", "cmsPropertyType.UniqueID",
"cmsPropertyType.validationRegExp", "cmsPropertyType.dataTypeId", "cmsPropertyType.sortOrder AS PropertyTypeSortOrder",
"cmsPropertyType.propertyTypeGroupId AS PropertyTypesGroupId", "cmsMemberType.memberCanEdit", "cmsMemberType.viewOnProfile",
"cmsPropertyType.propertyTypeGroupId AS PropertyTypesGroupId",
"cmsMemberType.memberCanEdit", "cmsMemberType.viewOnProfile", "cmsMemberType.isSensitive",
"uDataType.propertyEditorAlias", "uDataType.dbType", "cmsPropertyTypeGroup.id AS PropertyTypeGroupId",
"cmsPropertyTypeGroup.text AS PropertyGroupName", "cmsPropertyTypeGroup.uniqueID AS PropertyGroupUniqueID",
"cmsPropertyTypeGroup.sortorder AS PropertyGroupSortOrder", "cmsPropertyTypeGroup.contenttypeNodeId")

View File

@@ -20,6 +20,10 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
var name = Name;
// cater nodes with no name.
if (string.IsNullOrWhiteSpace(name))
return _numPos;
if (name[name.Length - 1] != ')')
return _numPos = -1;
@@ -106,7 +110,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
}
}
return uniqueing ? string.Concat(nodeName, " (", uniqueNumber.ToString(), ")") : nodeName;
return uniqueing || string.IsNullOrWhiteSpace(nodeName)
? string.Concat(nodeName, " (", uniqueNumber.ToString(), ")")
: nodeName;
}
}
}

View File

@@ -298,6 +298,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
entity.Id = id;
PersistAllowedSections(entity);
entity.ResetDirtyProperties();
}
protected override void PersistUpdatedItem(IUserGroup entity)
@@ -309,6 +311,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
Database.Update(userGroupDto);
PersistAllowedSections(entity);
entity.ResetDirtyProperties();
}
private void PersistAllowedSections(IUserGroup entity)

View File

@@ -7,6 +7,7 @@ using System.Web.Security;
using Newtonsoft.Json;
using NPoco;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Models.Membership;
@@ -151,14 +152,91 @@ ORDER BY colName";
return new Dictionary<UserState, int>
{
{UserState.All, result[0].num},
{UserState.Active, result[1].num},
{UserState.Disabled, result[2].num},
{UserState.LockedOut, result[3].num},
{UserState.Invited, result[4].num}
{UserState.All, (int) result[0].num},
{UserState.Active, (int) result[1].num},
{UserState.Disabled, (int) result[2].num},
{UserState.LockedOut, (int) result[3].num},
{UserState.Invited, (int) result[4].num}
};
}
public Guid CreateLoginSession(int userId, string requestingIpAddress, bool cleanStaleSessions = true)
{
//TODO: I know this doesn't follow the normal repository conventions which would require us to crete a UserSessionRepository
//and also business logic models for these objects but that's just so overkill for what we are doing
//and now that everything is properly in a transaction (Scope) there doesn't seem to be much reason for using that anymore
var now = DateTime.UtcNow;
var dto = new UserLoginDto
{
UserId = userId,
IpAddress = requestingIpAddress,
LoggedInUtc = now,
LastValidatedUtc = now,
LoggedOutUtc = null,
SessionId = Guid.NewGuid()
};
Database.Insert(dto);
if (cleanStaleSessions)
{
ClearLoginSessions(TimeSpan.FromDays(15));
}
return dto.SessionId;
}
public bool ValidateLoginSession(int userId, Guid sessionId)
{
var found = Database.FirstOrDefault<UserLoginDto>("WHERE sessionId=@sessionId", new {sessionId = sessionId});
if (found == null || found.UserId != userId || found.LoggedOutUtc.HasValue)
return false;
//now detect if there's been a timeout
if (DateTime.UtcNow - found.LastValidatedUtc > TimeSpan.FromMinutes(GlobalSettings.TimeOutInMinutes))
{
//timeout detected, update the record
ClearLoginSession(sessionId);
return false;
}
//update the validate date
found.LastValidatedUtc = DateTime.UtcNow;
Database.Update(found);
return true;
}
public int ClearLoginSessions(int userId)
{
//TODO: I know this doesn't follow the normal repository conventions which would require us to crete a UserSessionRepository
//and also business logic models for these objects but that's just so overkill for what we are doing
//and now that everything is properly in a transaction (Scope) there doesn't seem to be much reason for using that anymore
var count = Database.ExecuteScalar<int>("SELECT COUNT(*) FROM umbracoUserLogin WHERE userId=@userId", new { userId = userId });
Database.Execute("DELETE FROM umbracoUserLogin WHERE userId=@userId", new {userId = userId});
return count;
}
public int ClearLoginSessions(TimeSpan timespan)
{
//TODO: I know this doesn't follow the normal repository conventions which would require us to crete a UserSessionRepository
//and also business logic models for these objects but that's just so overkill for what we are doing
//and now that everything is properly in a transaction (Scope) there doesn't seem to be much reason for using that anymore
var fromDate = DateTime.UtcNow - timespan;
var count = Database.ExecuteScalar<int>("SELECT COUNT(*) FROM umbracoUserLogin WHERE lastValidatedUtc=@fromDate", new { fromDate = fromDate });
Database.Execute("DELETE FROM umbracoUserLogin WHERE lastValidatedUtc=@fromDate", new { fromDate = fromDate });
return count;
}
public void ClearLoginSession(Guid sessionId)
{
//TODO: I know this doesn't follow the normal repository conventions which would require us to crete a UserSessionRepository
//and also business logic models for these objects but that's just so overkill for what we are doing
//and now that everything is properly in a transaction (Scope) there doesn't seem to be much reason for using that anymore
Database.Execute("UPDATE umbracoUserLogin SET loggedOutUtc=@now WHERE sessionId=@sessionId",
new { now = DateTime.UtcNow, sessionId = sessionId });
}
protected override IEnumerable<IUser> PerformGetAll(params int[] ids)
{
var dtos = ids.Length == 0
@@ -429,7 +507,8 @@ ORDER BY colName";
{"updateDate", "UpdateDate"},
{"avatar", "Avatar"},
{"emailConfirmedDate", "EmailConfirmedDate"},
{"invitedDate", "InvitedDate"}
{"invitedDate", "InvitedDate"},
{"tourData", "TourData"}
};
// create list of properties that have changed

View File

@@ -373,6 +373,10 @@
<Compile Include="Persistence\Mappers\AuditEntryMapper.cs" />
<Compile Include="Persistence\Mappers\AuditMapper.cs" />
<Compile Include="Persistence\Mappers\ConsentMapper.cs" />
<Compile Include="Persistence\Repositories\IAuditEntryRepository.cs" />
<Compile Include="Persistence\Repositories\IConsentRepository.cs" />
<Compile Include="Persistence\Repositories\Implement\AuditEntryRepository.cs" />
<Compile Include="Persistence\Repositories\Implement\ConsentRepository.cs" />
<Compile Include="PropertyEditors\ConfigurationEditorOfTConfiguration.cs" />
<Compile Include="PropertyEditors\DataEditorAttribute.cs" />
<Compile Include="PropertyEditors\ConfigurationEditor.cs" />