Merge remote-tracking branch 'origin/6.2.0' into 7.1.2

Conflicts:
	build/Build.bat
	build/NuSpecs/UmbracoCms.Core.nuspec
	build/NuSpecs/build/UmbracoCms.targets
	src/Umbraco.Core/Models/IPublishedContentProperty.cs
	src/Umbraco.Core/Models/PublishedContent/IPublishedContentExtended.cs
	src/Umbraco.Core/Models/PublishedContent/PublishedContentExtended.cs
	src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs
	src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs
	src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs
	src/Umbraco.Core/Models/Template.cs
	src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs
	src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs
	src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs
	src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs
	src/Umbraco.Core/Persistence/Repositories/RecycleBinRepository.cs
	src/Umbraco.Core/Services/PackagingService.cs
	src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs
	src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs
	src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedFragment.cs
	src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs
	src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs
	src/Umbraco.Web/PublishedContentPropertyExtension.cs
	src/Umbraco.Web/Search/ExamineEvents.cs
	src/Umbraco.Web/UmbracoHelper.cs
	src/Umbraco.Web/umbraco.presentation/CompatibilityHelper.cs
	src/Umbraco.Web/umbraco.presentation/macro.cs
	src/umbraco.cms/businesslogic/template/Template.cs
This commit is contained in:
Shannon
2014-05-05 12:49:06 +10:00
31 changed files with 274 additions and 157 deletions

View File

@@ -472,6 +472,11 @@ namespace Umbraco.Core
string message;
var database = new UmbracoDatabase(_connectionString, ProviderName);
// If MySQL, we're going to ensure that database calls are maintaining proper casing as to remove the necessity for checks
// for case insensitive queries. In an ideal situation (which is what we're striving for), all calls would be case sensitive.
/*
var supportsCaseInsensitiveQueries = SqlSyntaxContext.SqlSyntaxProvider.SupportsCaseInsensitiveQueries(database);
if (supportsCaseInsensitiveQueries == false)
{
@@ -484,8 +489,9 @@ namespace Umbraco.Core
return new Result { Message = message, Success = false, Percentage = "15" };
}
*/
message = GetResultMessageForMySql(supportsCaseInsensitiveQueries);
message = GetResultMessageForMySql();
var schemaResult = ValidateDatabaseSchema();
var installedVersion = schemaResult.DetermineInstalledVersion();
@@ -536,9 +542,9 @@ namespace Umbraco.Core
LogHelper.Info<DatabaseContext>("Database upgrade started");
var database = new UmbracoDatabase(_connectionString, ProviderName);
var supportsCaseInsensitiveQueries = SqlSyntaxContext.SqlSyntaxProvider.SupportsCaseInsensitiveQueries(database);
//var supportsCaseInsensitiveQueries = SqlSyntaxContext.SqlSyntaxProvider.SupportsCaseInsensitiveQueries(database);
var message = GetResultMessageForMySql(supportsCaseInsensitiveQueries);
var message = GetResultMessageForMySql();
var schemaResult = ValidateDatabaseSchema();
var installedVersion = schemaResult.DetermineInstalledVersion();
@@ -565,6 +571,23 @@ namespace Umbraco.Core
}
}
private string GetResultMessageForMySql()
{
if (SqlSyntaxContext.SqlSyntaxProvider.GetType() == typeof(MySqlSyntaxProvider))
{
return "<p>&nbsp;</p><p>Congratulations, the database step ran successfully!</p>" +
"<p>Note: You're using MySQL and the database instance you're connecting to seems to support case insensitive queries.</p>" +
"<p>However, your hosting provider may not support this option. Umbraco does not currently support MySQL installs that do not support case insensitive queries</p>" +
"<p>Make sure to check with your hosting provider if they support case insensitive queries as well.</p>" +
"<p>They can check this by looking for the following setting in the my.ini file in their MySQL installation directory:</p>" +
"<pre>lower_case_table_names=1</pre><br />" +
"<p>For more technical information on case sensitivity in MySQL, have a look at " +
"<a href='http://dev.mysql.com/doc/refman/5.0/en/identifier-case-sensitivity.html'>the documentation on the subject</a></p>";
}
return string.Empty;
}
/*
private string GetResultMessageForMySql(bool? supportsCaseInsensitiveQueries)
{
if (supportsCaseInsensitiveQueries == null)
@@ -588,7 +611,7 @@ namespace Umbraco.Core
"<a href='http://dev.mysql.com/doc/refman/5.0/en/identifier-case-sensitivity.html'>the documentation on the subject</a></p>";
}
return string.Empty;
}
}*/
private Attempt<Result> CheckReadyForInstall()
{

View File

@@ -4,14 +4,14 @@ using System.Web;
namespace Umbraco.Core.Dynamics
{
internal class PropertyResult : IPublishedProperty, IHtmlString
internal class PropertyResult : IPublishedContentProperty, IHtmlString
{
private readonly IPublishedProperty _source;
private readonly IPublishedContentProperty _source;
private readonly string _alias;
private readonly object _value;
private readonly PropertyResultType _type;
internal PropertyResult(IPublishedProperty source, PropertyResultType type)
internal PropertyResult(IPublishedContentProperty source, PropertyResultType type)
{
if (source == null) throw new ArgumentNullException("source");

View File

@@ -5,6 +5,7 @@ using System.Runtime.Serialization;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Strings;
namespace Umbraco.Core.Models
{
@@ -35,7 +36,7 @@ namespace Umbraco.Core.Models
base.Path = path;
ParentId = -1;
_name = name; //.Replace("/", ".").Replace("\\", ""); // why? that's just the name!
_alias = alias.ToSafeAlias();
_alias = alias.ToCleanString(CleanStringType.UnderscoreAlias);
}
[DataMember]

View File

@@ -175,6 +175,8 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
.Where(x => x.IsNullOrWhiteSpace() == false).ToList();
//Add valid and invalid foreign key differences to the result object
// We'll need to do invariant contains with case insensitivity because foreign key, primary key, and even index naming w/ MySQL is not standardized
// In theory you could have: FK_ or fk_ ...or really any standard that your development department (or developer) chooses to use.
foreach (var unknown in unknownConstraintsInDatabase)
{
if (foreignKeysInSchema.InvariantContains(unknown) || primaryKeysInSchema.InvariantContains(unknown) || indexesInSchema.InvariantContains(unknown))

View File

@@ -65,7 +65,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
return new Version(4, 7, 0);
}
return new Version(4, 9, 0);
return new Version(4, 8, 0);
}
//if the error is for umbracoServer

View File

@@ -46,20 +46,20 @@ namespace Umbraco.Core.Persistence.Migrations
{
LogHelper.Info<MigrationRunner>("Initializing database migrations");
var foundMigrations = MigrationResolver.Current.Migrations.ToArray();
var foundMigrations = MigrationResolver.Current.Migrations.ToArray();
//filter all non-schema migrations
var migrations = isUpgrade
? OrderedUpgradeMigrations(foundMigrations).ToList()
: OrderedDowngradeMigrations(foundMigrations).ToList();
//SD: Why do we want this?
if (Migrating.IsRaisedEventCancelled(new MigrationEventArgs(migrations, _currentVersion, _targetVersion, true), this))
return false;
//Loop through migrations to generate sql
var migrationContext = InitializeMigrations(migrations, database, databaseProvider, isUpgrade);
try
{
ExecuteMigrations(migrationContext, database);
@@ -86,8 +86,8 @@ namespace Umbraco.Core.Persistence.Migrations
return true;
}
private void ExecuteMigrations(IMigrationContext context, Database database)
{
private void ExecuteMigrations(IMigrationContext context, Database database)
{
//Transactional execution of the sql that was generated from the found migrations
using (var transaction = database.GetTransaction())
{
@@ -108,13 +108,13 @@ namespace Umbraco.Core.Persistence.Migrations
transaction.Complete();
}
}
}
internal MigrationContext InitializeMigrations(List<IMigration> migrations, Database database, DatabaseProviders databaseProvider, bool isUpgrade = true)
{
{
//Loop through migrations to generate sql
var context = new MigrationContext(databaseProvider, database);
foreach (var migration in migrations)
{
var baseMigration = migration as MigrationBase;
@@ -148,7 +148,7 @@ namespace Umbraco.Core.Persistence.Migrations
}
return context;
}
}
/// <summary>
/// Filters and orders migrations based on the migrations listed and the currently configured version and the target installation version
@@ -158,17 +158,17 @@ namespace Umbraco.Core.Persistence.Migrations
internal IEnumerable<IMigration> OrderedUpgradeMigrations(IEnumerable<IMigration> foundMigrations)
{
var migrations = (from migration in foundMigrations
let migrationAttributes = migration.GetType().GetCustomAttributes<MigrationAttribute>(false)
from migrationAttribute in migrationAttributes
where migrationAttribute != null
where
let migrationAttributes = migration.GetType().GetCustomAttributes<MigrationAttribute>(false)
from migrationAttribute in migrationAttributes
where migrationAttribute != null
where
migrationAttribute.TargetVersion > _currentVersion &&
migrationAttribute.TargetVersion <= _targetVersion &&
migrationAttribute.TargetVersion <= _targetVersion &&
migrationAttribute.ProductName == _productName &&
//filter if the migration specifies a minimum current version for which to execute
(migrationAttribute.MinimumCurrentVersion == null || _currentVersion >= migrationAttribute.MinimumCurrentVersion)
orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder ascending
select migration).Distinct();
orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder ascending
select migration).Distinct();
return migrations;
}
@@ -180,17 +180,17 @@ namespace Umbraco.Core.Persistence.Migrations
public IEnumerable<IMigration> OrderedDowngradeMigrations(IEnumerable<IMigration> foundMigrations)
{
var migrations = (from migration in foundMigrations
let migrationAttributes = migration.GetType().GetCustomAttributes<MigrationAttribute>(false)
from migrationAttribute in migrationAttributes
where migrationAttribute != null
where
let migrationAttributes = migration.GetType().GetCustomAttributes<MigrationAttribute>(false)
from migrationAttribute in migrationAttributes
where migrationAttribute != null
where
migrationAttribute.TargetVersion > _currentVersion &&
migrationAttribute.TargetVersion <= _targetVersion &&
migrationAttribute.TargetVersion <= _targetVersion &&
migrationAttribute.ProductName == _productName &&
//filter if the migration specifies a minimum current version for which to execute
(migrationAttribute.MinimumCurrentVersion == null || _currentVersion >= migrationAttribute.MinimumCurrentVersion)
orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder descending
select migration).Distinct();
orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder descending
select migration).Distinct();
return migrations;
}

View File

@@ -5,6 +5,7 @@ using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero
{
//see: http://issues.umbraco.org/issue/U4-4430
[Migration("7.1.0", 0, GlobalSettings.UmbracoMigrationName)]
[Migration("6.2.0", 0, GlobalSettings.UmbracoMigrationName)]
@@ -15,15 +16,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero
if (Context.CurrentDatabaseProvider == DatabaseProviders.MySql)
{
var constraints = SqlSyntaxContext.SqlSyntaxProvider.GetConstraintsPerColumn(Context.Database).Distinct().ToArray();
//This should be 2 because this table has 2 keys
if (constraints.Count(x => x.Item1.InvariantEquals("cmsContentType2ContentType") && x.Item3.InvariantEquals("PRIMARY")) == 0)
{
Create.PrimaryKey("PK_cmsContentType2ContentType")
.OnTable("cmsContentType2ContentType")
.Columns(new[] {"parentContentTypeId", "childContentTypeId"});
}
//This should be 2 because this table has 2 keys
if (constraints.Count(x => x.Item1.InvariantEquals("cmsContentTypeAllowedContentType") && x.Item3.InvariantEquals("PRIMARY")) == 0)
{

View File

@@ -0,0 +1,34 @@
using System.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero
{
//We have to target this specifically to ensure this DOES NOT execute if upgrading from a version previous to 6.0,
// this is because when the 6.0.0 migrations are executed, this primary key get's created so if this migration is also executed
// we will get exceptions because it is trying to create the PK two times.
[Migration("6.0.0", "6.2.0", 0, GlobalSettings.UmbracoMigrationName)]
public class AssignMissingPrimaryForMySqlKeys2 : MigrationBase
{
public override void Up()
{
if (Context.CurrentDatabaseProvider == DatabaseProviders.MySql)
{
var constraints = SqlSyntaxContext.SqlSyntaxProvider.GetConstraintsPerColumn(Context.Database).Distinct().ToArray();
//This should be 2 because this table has 2 keys
if (constraints.Count(x => x.Item1.InvariantEquals("cmsContentType2ContentType") && x.Item3.InvariantEquals("PRIMARY")) == 0)
{
Create.PrimaryKey("PK_cmsContentType2ContentType")
.OnTable("cmsContentType2ContentType")
.Columns(new[] {"parentContentTypeId", "childContentTypeId"});
}
}
}
public override void Down()
{
//don't do anything, these keys should have always existed!
}
}
}

View File

@@ -158,12 +158,12 @@ namespace Umbraco.Core.Persistence.Repositories
"DELETE FROM umbracoRelation WHERE childId = @Id",
"DELETE FROM cmsTagRelationship WHERE nodeId = @Id",
"DELETE FROM umbracoDomains WHERE domainRootStructureID = @Id",
"DELETE FROM cmsDocument WHERE NodeId = @Id",
"DELETE FROM cmsDocument WHERE nodeId = @Id",
"DELETE FROM cmsPropertyData WHERE contentNodeId = @Id",
"DELETE FROM cmsPreviewXml WHERE nodeId = @Id",
"DELETE FROM cmsContentVersion WHERE ContentId = @Id",
"DELETE FROM cmsContentXml WHERE nodeID = @Id",
"DELETE FROM cmsContent WHERE NodeId = @Id",
"DELETE FROM cmsContentXml WHERE nodeId = @Id",
"DELETE FROM cmsContent WHERE nodeId = @Id",
"DELETE FROM umbracoNode WHERE id = @Id"
};
return list;

View File

@@ -153,7 +153,7 @@ namespace Umbraco.Core.Persistence.Repositories
"DELETE FROM cmsPropertyType WHERE contentTypeId = @Id",
"DELETE FROM cmsPropertyTypeGroup WHERE contenttypeNodeId = @Id",
"DELETE FROM cmsDocumentType WHERE contentTypeNodeId = @Id",
"DELETE FROM cmsContentType WHERE NodeId = @Id",
"DELETE FROM cmsContentType WHERE nodeId = @Id",
"DELETE FROM umbracoNode WHERE id = @Id"
};
return list;

View File

@@ -142,12 +142,12 @@ namespace Umbraco.Core.Persistence.Repositories
"DELETE FROM umbracoRelation WHERE parentId = @Id",
"DELETE FROM umbracoRelation WHERE childId = @Id",
"DELETE FROM cmsTagRelationship WHERE nodeId = @Id",
"DELETE FROM cmsDocument WHERE NodeId = @Id",
"DELETE FROM cmsDocument WHERE nodeId = @Id",
"DELETE FROM cmsPropertyData WHERE contentNodeId = @Id",
"DELETE FROM cmsPreviewXml WHERE nodeId = @Id",
"DELETE FROM cmsContentVersion WHERE ContentId = @Id",
"DELETE FROM cmsContentXml WHERE nodeID = @Id",
"DELETE FROM cmsContent WHERE NodeId = @Id",
"DELETE FROM cmsContentXml WHERE nodeId = @Id",
"DELETE FROM cmsContent WHERE nodeId = @Id",
"DELETE FROM umbracoNode WHERE id = @Id"
};
return list;

View File

@@ -134,7 +134,7 @@ namespace Umbraco.Core.Persistence.Repositories
"DELETE FROM cmsContentType2ContentType WHERE childContentTypeId = @Id",
"DELETE FROM cmsPropertyType WHERE contentTypeId = @Id",
"DELETE FROM cmsPropertyTypeGroup WHERE contenttypeNodeId = @Id",
"DELETE FROM cmsContentType WHERE NodeId = @Id",
"DELETE FROM cmsContentType WHERE nodeId = @Id",
"DELETE FROM umbracoNode WHERE id = @Id"
};
return list;

View File

@@ -120,7 +120,8 @@ namespace Umbraco.Core.Persistence.Repositories
group.AddingEntity();
var dto = _modelFactory.BuildDto(group);
var o = Database.IsNew(dto) ? Convert.ToInt32(Database.Insert(dto)) : Database.Update(dto);
group.Id = dto.NodeId; //Set Id on entity to ensure an Id is set
//Update with new correct path and id
dto.Path = string.Concat("-1,", dto.NodeId);
Database.Update(dto);

View File

@@ -189,8 +189,8 @@ namespace Umbraco.Core.Persistence.Repositories
"DELETE FROM cmsMember2MemberGroup WHERE Member = @Id",
"DELETE FROM cmsMember WHERE nodeId = @Id",
"DELETE FROM cmsContentVersion WHERE ContentId = @Id",
"DELETE FROM cmsContentXml WHERE nodeID = @Id",
"DELETE FROM cmsContent WHERE NodeId = @Id",
"DELETE FROM cmsContentXml WHERE nodeId = @Id",
"DELETE FROM cmsContent WHERE nodeId = @Id",
"DELETE FROM umbracoNode WHERE id = @Id"
};
return list;

View File

@@ -149,7 +149,7 @@ namespace Umbraco.Core.Persistence.Repositories
"DELETE FROM cmsPropertyType WHERE contentTypeId = @Id",
"DELETE FROM cmsPropertyTypeGroup WHERE contenttypeNodeId = @Id",
"DELETE FROM cmsMemberType WHERE NodeId = @Id",
"DELETE FROM cmsContentType WHERE NodeId = @Id",
"DELETE FROM cmsContentType WHERE nodeId = @Id",
"DELETE FROM umbracoNode WHERE id = @Id"
};
return list;

View File

@@ -86,12 +86,12 @@ namespace Umbraco.Core.Persistence.Repositories
FormatDeleteStatement("umbracoRelation", "childId"),
FormatDeleteStatement("cmsTagRelationship", "nodeId"),
FormatDeleteStatement("umbracoDomains", "domainRootStructureID"),
FormatDeleteStatement("cmsDocument", "NodeId"),
FormatDeleteStatement("cmsDocument", "nodeId"),
FormatDeleteStatement("cmsPropertyData", "contentNodeId"),
FormatDeleteStatement("cmsPreviewXml", "nodeId"),
FormatDeleteStatement("cmsContentVersion", "ContentId"),
FormatDeleteStatement("cmsContentXml", "nodeID"),
FormatDeleteStatement("cmsContent", "NodeId"),
FormatDeleteStatement("cmsContentXml", "nodeId"),
FormatDeleteStatement("cmsContent", "nodeId"),
"UPDATE umbracoNode SET parentID = '-20' WHERE trashed = '1' AND nodeObjectType = @NodeObjectType",
"DELETE FROM umbracoNode WHERE trashed = '1' AND nodeObjectType = @NodeObjectType"
};

View File

@@ -136,7 +136,7 @@ namespace Umbraco.Core.Persistence.Repositories
"DELETE FROM cmsTask WHERE parentUserId = @Id",
"DELETE FROM umbracoUser2NodePermission WHERE userId = @Id",
"DELETE FROM umbracoUser2NodeNotify WHERE userId = @Id",
"DELETE FROM umbracoUserLogins WHERE userId = @Id",
"DELETE FROM umbracoUserLogins WHERE userID = @Id",
"DELETE FROM umbracoUser2app WHERE " + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("user") + "=@Id",
"DELETE FROM umbracoUser WHERE id = @Id"
};

View File

@@ -943,11 +943,12 @@ namespace Umbraco.Core.Services
return ImportDictionaryItems(dictionaryItemElementList, languages, raiseEvents);
}
private IEnumerable<IDictionaryItem> ImportDictionaryItems(XElement dictionaryItemElementList, List<ILanguage> languages, bool raiseEvents)
private IEnumerable<IDictionaryItem> ImportDictionaryItems(XElement dictionaryItemElementList, List<ILanguage> languages, bool raiseEvents, Guid? parentId = null)
{
var items = new List<IDictionaryItem>();
foreach (var dictionaryItemElement in dictionaryItemElementList.Elements("DictionaryItem"))
items.AddRange(ImportDictionaryItem(dictionaryItemElement, languages, raiseEvents));
items.AddRange(ImportDictionaryItem(dictionaryItemElement, languages, raiseEvents, parentId));
if (raiseEvents)
ImportedDictionaryItem.RaiseEvent(new ImportEventArgs<IDictionaryItem>(items, dictionaryItemElementList, false), this);
@@ -955,7 +956,7 @@ namespace Umbraco.Core.Services
return items;
}
private IEnumerable<IDictionaryItem> ImportDictionaryItem(XElement dictionaryItemElement, List<ILanguage> languages, bool raiseEvents)
private IEnumerable<IDictionaryItem> ImportDictionaryItem(XElement dictionaryItemElement, List<ILanguage> languages, bool raiseEvents, Guid? parentId)
{
var items = new List<IDictionaryItem>();
@@ -964,10 +965,11 @@ namespace Umbraco.Core.Services
if (_localizationService.DictionaryItemExists(key))
dictionaryItem = GetAndUpdateDictionaryItem(key, dictionaryItemElement, languages);
else
dictionaryItem = CreateNewDictionaryItem(key, dictionaryItemElement, languages);
dictionaryItem = CreateNewDictionaryItem(key, dictionaryItemElement, languages, parentId);
_localizationService.Save(dictionaryItem);
items.Add(dictionaryItem);
items.AddRange(ImportDictionaryItems(dictionaryItemElement, languages, raiseEvents));
items.AddRange(ImportDictionaryItems(dictionaryItemElement, languages, raiseEvents, dictionaryItem.Key));
return items;
}
@@ -981,9 +983,9 @@ namespace Umbraco.Core.Services
return dictionaryItem;
}
private static DictionaryItem CreateNewDictionaryItem(string key, XElement dictionaryItemElement, List<ILanguage> languages)
private static DictionaryItem CreateNewDictionaryItem(string key, XElement dictionaryItemElement, List<ILanguage> languages, Guid? parentId)
{
var dictionaryItem = new DictionaryItem(key);
var dictionaryItem = parentId.HasValue ? new DictionaryItem(parentId.Value, key) : new DictionaryItem(key);
var translations = new List<IDictionaryTranslation>();
foreach (var valueElement in dictionaryItemElement.Elements("Value"))

View File

@@ -92,7 +92,7 @@ namespace Umbraco.Core.Strings
/// <summary>
/// Flag mask for role.
/// </summary>
RoleMask = UrlSegment | Alias | FileName | ConvertCase,
RoleMask = UrlSegment | Alias | UnderscoreAlias | FileName | ConvertCase,
/// <summary>
/// Url role.
@@ -112,6 +112,12 @@ namespace Umbraco.Core.Strings
/// <summary>
/// ConvertCase role.
/// </summary>
ConvertCase = 0x080000
ConvertCase = 0x080000,
/// <summary>
/// UnderscoreAlias role.
/// </summary>
/// <remarks>This is Alias + leading underscore.</remarks>
UnderscoreAlias = 0x100000
}
}

View File

@@ -175,6 +175,12 @@ namespace Umbraco.Core.Strings
: (char.IsLetterOrDigit(c) || c == '_'), // letter, digit or underscore
StringType = CleanStringType.Ascii | CleanStringType.UmbracoCase,
BreakTermsOnUpper = false
}).WithConfig(CleanStringType.UnderscoreAlias, new Config
{
PreFilter = ApplyUrlReplaceCharacters,
IsTerm = (c, leading) => char.IsLetterOrDigit(c) || c == '_', // letter, digit or underscore
StringType = CleanStringType.Ascii | CleanStringType.UmbracoCase,
BreakTermsOnUpper = false
}).WithConfig(CleanStringType.ConvertCase, new Config
{
PreFilter = null,

View File

@@ -342,6 +342,7 @@
<Compile Include="Models\EntityExtensions.cs" />
<Compile Include="Models\Folder.cs" />
<Compile Include="Models\IMemberGroup.cs" />
<Compile Include="Models\IPublishedContentProperty.cs" />
<Compile Include="Models\IRelation.cs" />
<Compile Include="Models\IRelationType.cs" />
<Compile Include="Models\Membership\MembershipScenario.cs" />
@@ -373,6 +374,7 @@
<Compile Include="Persistence\Migrations\DataLossException.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenOneZero\AssignMissingPrimaryForMySqlKeys.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSixTwoZero\AssignMissingPrimaryForMySqlKeys.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSixTwoZero\AssignMissingPrimaryForMySqlKeys2.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSixTwoZero\ChangePasswordColumn.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSixTwoZero\UpdateToNewMemberPropertyAliases.cs" />
<Compile Include="Persistence\DbConnectionExtensions.cs" />
@@ -955,7 +957,6 @@
<Compile Include="Dynamics\Signature.cs" />
<Compile Include="ExpressionExtensions.cs" />
<Compile Include="Models\IPublishedContent.cs" />
<Compile Include="Models\IPublishedProperty.cs" />
<Compile Include="ActionsResolver.cs" />
<Compile Include="CacheRefreshersResolver.cs" />
<Compile Include="Configuration\GlobalSettings.cs" />

View File

@@ -16,7 +16,7 @@ namespace Umbraco.Tests.Migrations
{
var runner = new MigrationRunner(new Version(4, 0, 0), new Version(6, 0, 0), "Test");
var migrations = runner.OrderedUpgradeMigrations(new List<IMigration> {new MultiMigration()});
var migrations = runner.OrderedUpgradeMigrations(new List<IMigration> { new MultiMigration() });
var ctx = runner.InitializeMigrations(
//new List<IMigration> {new DoRunMigration(), new DoNotRunMigration()},
@@ -58,7 +58,7 @@ namespace Umbraco.Tests.Migrations
Assert.AreEqual(1, ctx.Expressions.Count());
}
[Migration("6.0.0", 1, "Test")]
[Migration("5.0.0", 1, "Test")]
private class MultiMigration : MigrationBase

View File

@@ -414,6 +414,32 @@ namespace Umbraco.Tests.Services.Importing
AssertDictionaryItem("Child", expectedNorwegianChildValue, "nb-NO");
}
[Test]
public void PackagingService_Can_Import_Nested_DictionaryItems()
{
// Arrange
const string parentKey = "Parent";
const string childKey = "Child";
var newPackageXml = XElement.Parse(ImportResources.Dictionary_Package);
var dictionaryItemsElement = newPackageXml.Elements("DictionaryItems").First();
AddLanguages();
// Act
var dictionaryItems = ServiceContext.PackagingService.ImportDictionaryItems(dictionaryItemsElement);
// Assert
Assert.That(ServiceContext.LocalizationService.DictionaryItemExists(parentKey), "DictionaryItem parentKey does not exist");
Assert.That(ServiceContext.LocalizationService.DictionaryItemExists(childKey), "DictionaryItem childKey does not exist");
var parentDictionaryItem = ServiceContext.LocalizationService.GetDictionaryItemByKey(parentKey);
var childDictionaryItem = ServiceContext.LocalizationService.GetDictionaryItemByKey(childKey);
Assert.That(parentDictionaryItem.ParentId, Is.Not.EqualTo(childDictionaryItem.ParentId));
Assert.That(childDictionaryItem.ParentId, Is.EqualTo(parentDictionaryItem.Key));
}
[Test]
public void PackagingService_WhenExistingDictionaryKey_ImportsNewChildren()
{

View File

@@ -121,7 +121,12 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
try
{
//by default use the InternalSearcher
return eMgr.IndexProviderCollection["InternalIndexer"];
var indexer = eMgr.IndexProviderCollection["InternalIndexer"];
if (indexer.IndexerData.IncludeNodeTypes.Any() || indexer.IndexerData.ExcludeNodeTypes.Any())
{
LogHelper.Warn<PublishedMediaCache>("The InternalIndexer for examine is configured incorrectly, it should not list any include/exclude node types or field names, it should simply be configured as: " + "<IndexSet SetName=\"InternalIndexSet\" IndexPath=\"~/App_Data/TEMP/ExamineIndexes/Internal/\" />");
}
return indexer;
}
catch (Exception ex)
{
@@ -177,12 +182,13 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
return ConvertFromSearchResult(results.First());
}
}
catch (FileNotFoundException)
catch (FileNotFoundException ex)
{
//Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco
//See this thread: http://examine.cdodeplex.com/discussions/264341
//Catch the exception here for the time being, and just fallback to GetMedia
//TODO: Need to fix examine in LB scenarios!
LogHelper.Error<PublishedMediaCache>("Could not load data from Examine index for media", ex);
}
}
@@ -259,7 +265,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
var values = new Dictionary<string, string> {{"nodeName", xpath.GetAttribute("nodeName", "")}};
if (!UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema)
{
values.Add("nodeTypeAlias", xpath.Name);
values["nodeTypeAlias"] = xpath.Name;
}
var result = xpath.SelectChildren(XPathNodeType.Element);
@@ -271,13 +277,13 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
//checking for duplicate keys because of the 'nodeTypeAlias' might already be added above.
if (!values.ContainsKey(result.Current.Name))
{
values.Add(result.Current.Name, result.Current.Value);
values[result.Current.Name] = result.Current.Value;
}
while (result.Current.MoveToNextAttribute())
{
if (!values.ContainsKey(result.Current.Name))
{
values.Add(result.Current.Name, result.Current.Value);
values[result.Current.Name] = result.Current.Value;
}
}
result.Current.MoveToParent();
@@ -296,7 +302,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
value = result.Current.OuterXml;
}
}
values.Add(result.Current.Name, value);
values[result.Current.Name] = value;
}
}
@@ -320,48 +326,25 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
/// <returns></returns>
private IPublishedProperty GetProperty(DictionaryPublishedContent dd, string alias)
{
if (dd.LoadedFromExamine)
{
//if this is from Examine, lets check if the alias does not exist on the document
if (dd.Properties.All(x => x.PropertyTypeAlias != alias))
{
//ok it doesn't exist, we might assume now that Examine didn't index this property because the index is not set up correctly
//so before we go loading this from the database, we can check if the alias exists on the content type at all, this information
//is cached so will be quicker to look up.
if (dd.Properties.Any(x => x.PropertyTypeAlias == UmbracoContentIndexer.NodeTypeAliasFieldName))
{
// so in dd.Properties, there is an IPublishedProperty with property type alias "__NodeTypeAlias" and
// that special property would contain the node type alias, which we use to get "aliases & names". That
// special property is going to be a PropertyResult (with Value == DataValue) and we
// want its value in the most simple way = it is OK to use DataValue here.
var aliasesAndNames = ContentType.GetAliasesAndNames(dd.Properties.First(x => x.PropertyTypeAlias.InvariantEquals(UmbracoContentIndexer.NodeTypeAliasFieldName)).DataValue.ToString());
if (aliasesAndNames != null)
{
if (!aliasesAndNames.ContainsKey(alias))
{
//Ok, now we know it doesn't exist on this content type anyways
return null;
}
}
}
//lets check if the alias does not exist on the document.
//NOTE: Examine will not index empty values and we do not output empty XML Elements to the cache - either of these situations
// would mean that the property is missing from the collection whether we are getting the value from Examine or from the library media cache.
if (dd.Properties.All(x => x.PropertyTypeAlias != alias))
{
return null;
}
//if we've made it here, that means it does exist on the content type but not in examine, we'll need to query the db :(
var media = global::umbraco.library.GetMedia(dd.Id, true);
if (media != null && media.Current != null)
{
media.MoveNext();
var mediaDoc = ConvertFromXPathNavigator(media.Current);
return mediaDoc.Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(alias));
}
}
}
//We've made it here which means that the value is stored in the Examine index.
//We are going to check for a special field however, that is because in some cases we store a 'Raw'
//value in the index such as for xml/html.
var rawValue = dd.Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(UmbracoContentIndexer.RawFieldPrefix + alias));
return rawValue
?? dd.Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(alias));
if (dd.LoadedFromExamine)
{
//We are going to check for a special field however, that is because in some cases we store a 'Raw'
//value in the index such as for xml/html.
var rawValue = dd.Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(UmbracoContentIndexer.RawFieldPrefix + alias));
return rawValue
?? dd.Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(alias));
}
//if its not loaded from examine, then just return the property
return dd.Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(alias));
}
/// <summary>

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Globalization;
using System.Linq;
using System.Security;
@@ -60,6 +60,7 @@ namespace Umbraco.Web.Search
CacheRefresherBase<PageCacheRefresher>.CacheUpdated += PublishedPageCacheRefresherCacheUpdated;
CacheRefresherBase<MediaCacheRefresher>.CacheUpdated += MediaCacheRefresherCacheUpdated;
CacheRefresherBase<MemberCacheRefresher>.CacheUpdated += MemberCacheRefresherCacheUpdated;
CacheRefresherBase<ContentTypeCacheRefresher>.CacheUpdated += ContentTypeCacheRefresherCacheUpdated;
var contentIndexer = ExamineManager.Instance.IndexProviderCollection["InternalIndexer"] as UmbracoContentIndexer;
if (contentIndexer != null)
@@ -73,6 +74,24 @@ namespace Umbraco.Web.Search
}
}
/// <summary>
/// This is used to refresh content indexers IndexData based on the DataService whenever a content type is changed since
/// properties may have been added/removed
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <remarks>
/// See: http://issues.umbraco.org/issue/U4-4798
/// </remarks>
static void ContentTypeCacheRefresherCacheUpdated(ContentTypeCacheRefresher sender, CacheRefresherEventArgs e)
{
var indexersToUpdated = ExamineManager.Instance.IndexProviderCollection.OfType<UmbracoContentIndexer>();
foreach (var provider in indexersToUpdated)
{
provider.RefreshIndexerDataFromDataService();
}
}
static void MemberCacheRefresherCacheUpdated(MemberCacheRefresher sender, CacheRefresherEventArgs e)
{
switch (e.MessageType)

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
@@ -254,7 +255,7 @@ namespace Umbraco.Web
//NOTE: the value could have html encoded values, so we need to deal with that
macroProps.Add(i.Key.ToLower(), (i.Value is string) ? HttpUtility.HtmlDecode(i.Value.ToString()) : i.Value);
macroProps.Add(i.Key.ToLowerInvariant(), (i.Value is string) ? HttpUtility.HtmlDecode(i.Value.ToString()) : i.Value);
}
var macroControl = m.renderMacro(macroProps,
umbracoPage.Elements,

View File

@@ -803,9 +803,9 @@ namespace umbraco
{
foreach (MacroPropertyModel mp in Model.Properties)
{
if (attributes.ContainsKey(mp.Key.ToLower()))
if (attributes.ContainsKey(mp.Key.ToLowerInvariant()))
{
var item = attributes[mp.Key.ToLower()];
var item = attributes[mp.Key.ToLowerInvariant()];
mp.Value = item == null ? string.Empty : item.ToString();
}

View File

@@ -14,58 +14,63 @@ namespace UmbracoExamine.Config
/// </summary>
public static class IndexSetExtensions
{
private static readonly object Locker = new object();
internal static IIndexCriteria ToIndexCriteria(this IndexSet set, IDataService svc,
IEnumerable<StaticField> indexFieldPolicies)
{
var attributeFields = set.IndexAttributeFields.Cast<IIndexField>().ToArray();
var userFields = set.IndexUserFields.Cast<IIndexField>().ToArray();
var includeNodeTypes = set.IncludeNodeTypes.ToList().Select(x => x.Name).ToArray();
var excludeNodeTypes = set.ExcludeNodeTypes.ToList().Select(x => x.Name).ToArray();
var parentId = set.IndexParentId;
//if there are no user fields defined, we'll populate them from the data source (include them all)
if (set.IndexUserFields.Count == 0)
{
lock (Locker)
//we need to add all user fields to the collection if it is empty (this is the default if none are specified)
var userProps = svc.ContentService.GetAllUserPropertyNames();
var fields = new List<IIndexField>();
foreach (var u in userProps)
{
//we need to add all user fields to the collection if it is empty (this is the default if none are specified)
var userFields = svc.ContentService.GetAllUserPropertyNames();
foreach (var u in userFields)
var field = new IndexField() { Name = u };
var policy = indexFieldPolicies.FirstOrDefault(x => x.Name == u);
if (policy != null)
{
var field = new IndexField() {Name = u};
var policy = indexFieldPolicies.FirstOrDefault(x => x.Name == u);
if (policy != null)
{
field.Type = policy.Type;
field.EnableSorting = policy.EnableSorting;
}
set.IndexUserFields.Add(field);
field.Type = policy.Type;
field.EnableSorting = policy.EnableSorting;
}
fields.Add(field);
}
userFields = fields.ToArray();
}
//if there are no attribute fields defined, we'll populate them from the data source (include them all)
if (set.IndexAttributeFields.Count == 0)
{
lock (Locker)
//we need to add all system fields to the collection if it is empty (this is the default if none are specified)
var sysProps = svc.ContentService.GetAllSystemPropertyNames();
var fields = new List<IIndexField>();
foreach (var s in sysProps)
{
//we need to add all system fields to the collection if it is empty (this is the default if none are specified)
var sysFields = svc.ContentService.GetAllSystemPropertyNames();
foreach (var s in sysFields)
var field = new IndexField() { Name = s };
var policy = indexFieldPolicies.FirstOrDefault(x => x.Name == s);
if (policy != null)
{
var field = new IndexField() { Name = s };
var policy = indexFieldPolicies.FirstOrDefault(x => x.Name == s);
if (policy != null)
{
field.Type = policy.Type;
field.EnableSorting = policy.EnableSorting;
}
set.IndexAttributeFields.Add(field);
field.Type = policy.Type;
field.EnableSorting = policy.EnableSorting;
}
fields.Add(field);
}
attributeFields = fields.ToArray();
}
return new IndexCriteria(
set.IndexAttributeFields.Cast<IIndexField>().ToArray(),
set.IndexUserFields.Cast<IIndexField>().ToArray(),
set.IncludeNodeTypes.ToList().Select(x => x.Name).ToArray(),
set.ExcludeNodeTypes.ToList().Select(x => x.Name).ToArray(),
set.IndexParentId);
attributeFields,
userFields,
includeNodeTypes,
excludeNodeTypes,
parentId);
}
/// <summary>

View File

@@ -298,6 +298,19 @@ namespace UmbracoExamine
base.RebuildIndex();
}
/// <summary>
/// Used to refresh the current IndexerData from the data in the DataService. This can be used
/// if there are more properties added/removed from the database
/// </summary>
public void RefreshIndexerDataFromDataService()
{
//TODO: This would be much better done if the IndexerData property had read/write locks applied
// to it! Unless we update the base class there's really no way to prevent the IndexerData from being
// changed during an operation that is reading from it.
var newIndexerData = GetIndexerData(IndexSets.Instance.Sets[IndexSetName]);
IndexerData = newIndexerData;
}
/// <summary>
/// Override this method to strip all html from all user fields before raising the event, then after the event
/// ensure our special Path field is added to the collection

View File

@@ -7,6 +7,7 @@ using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Strings;
using umbraco.DataLayer;
using System.Text.RegularExpressions;
using System.IO;
@@ -205,7 +206,7 @@ namespace umbraco.cms.businesslogic.template
{
FlushCache();
_oldAlias = _alias;
_alias = value.ToSafeAlias();
_alias = value.ToCleanString(CleanStringType.UnderscoreAlias);
SqlHelper.ExecuteNonQuery("Update cmsTemplate set alias = @alias where NodeId = " + this.Id, SqlHelper.CreateParameter("@alias", _alias));
_templateAliasesInitialized = false;
@@ -403,13 +404,13 @@ namespace umbraco.cms.businesslogic.template
var node = MakeNew(-1, ObjectType, u.Id, 1, name, Guid.NewGuid());
//ensure unique alias
name = name.ToSafeAlias();
name = name.ToCleanString(CleanStringType.UnderscoreAlias);
if (GetByAlias(name) != null)
name = EnsureUniqueAlias(name, 1);
//name = name.Replace("/", ".").Replace("\\", ""); //why? ToSafeAlias() already removes those chars
if (name.Length > 100)
name = name.Substring(0, 98); // + "..."; // no, these are invalid alias chars
name = name.Substring(0, 95); // + "..."; // no, these are invalid alias chars
SqlHelper.ExecuteNonQuery("INSERT INTO cmsTemplate (NodeId, Alias, design, master) VALUES (@nodeId, @alias, @design, @master)",
SqlHelper.CreateParameter("@nodeId", node.Id),

View File

@@ -1022,10 +1022,10 @@ namespace umbraco.cms.businesslogic.web
return result;
}
return Attempt<PublishStatus>.Fail();
return new Attempt<PublishStatus>(false, new PublishStatus(Content, PublishStatusType.FailedCancelledByEvent));
}
return Attempt<PublishStatus>.Fail();
return new Attempt<PublishStatus>(false, new PublishStatus(Content, PublishStatusType.FailedCancelledByEvent));
}
/// <summary>