Compare commits

...

114 Commits

Author SHA1 Message Date
Sebastiaan Janssen 52dce66598 Bump version 2017-02-21 08:49:34 +01:00
Sebastiaan Janssen acf10eeef0 U4-9532 logic in ContentRepository.ClearPublished is inefficient - cherry picked from 7.6.0 2017-02-16 08:11:35 +01:00
Sebastiaan Janssen 759e444624 Merge pull request #1467 from ArnoldV/dev-v7
U4-8990 Updated Dutch Language File
2017-02-15 22:39:44 +01:00
Sebastiaan Janssen a68f4e284a Merge pull request #1752 from umbraco/temp-U4-9517
U4-9517 BackOfficeSignInManager doesn't automatically keep LastLoginD…
2017-02-13 10:38:41 +01:00
Shannon d554417fde U4-9517 BackOfficeSignInManager doesn't automatically keep LastLoginDate set on users 2017-02-13 16:47:04 +11:00
Claus eedcfc122b Merge pull request #1747 from umbraco/temp-U4-9513
U4-9513 Empty recycle bin SQL logic can SQL to timeout due to query i…
2017-02-10 11:42:14 +01:00
Claus 8cf3110708 fixing typo and missing file ref in csproj. 2017-02-10 11:41:46 +01:00
Shannon dbe489c0f2 U4-9513 Empty recycle bin SQL logic can SQL to timeout due to query inefficiencies 2017-02-10 12:27:50 +11:00
Sebastiaan Janssen a7e28a97d9 Merge pull request #1715 from umbraco/temp-U4-9450
U4-9450 Paged data queries return all property data in the entire dat…
2017-02-09 21:02:15 +01:00
Sebastiaan Janssen ade3c4ee6c Merge pull request #1732 from rsoeteman/U4-9493
U4 9493
2017-02-09 14:26:44 +01:00
Arnold Visser 7da7c1e5a1 Addes all new/missing fields since last edit
Checked against the English version
2017-02-09 13:47:31 +01:00
Shannon Deminick 55098deac0 Merge pull request #1728 from umbraco/temp-u4-9487
U4-9487 - optimize initialization of xml nodes
2017-02-09 16:56:05 +11:00
Shannon 58c5618d30 Adds benchmarks for XML perf testing - had to make the init xml code static with out params to test but that's ok. 2017-02-09 16:42:59 +11:00
Shannon d0cbeb4a0a Better optimized initialization of xml nodes with less allocations 2017-02-09 15:29:38 +11:00
Mikkel Holck Madsen a8914dcfdb Merge pull request #1740 from umbraco/temp-U4-9458
Adds unit tests for U4-9458 UmbracoExamine performance optimizations
2017-02-08 15:12:07 +01:00
Sebastiaan Janssen 73e219b23c Fix unit tests 2017-02-07 16:24:34 +01:00
Sebastiaan Janssen 07e0de5824 Adds unit tests for U4-9458 UmbracoExamine performance optimizations 2017-02-07 16:20:40 +01:00
Sebastiaan Janssen 389b29599b U4-9495 ImageProcessor high CPU usage when trimCache=true 2017-02-07 10:29:11 +01:00
Shannon f816a63b54 Ok, reverting the correct commit this time: Revert "Fixes database context issue during startup when there is no db configured" 2017-02-07 12:49:26 +11:00
Shannon fa8f3985b8 Fixes database context issue during startup when there is no db configured 2017-02-07 12:33:37 +11:00
Mikkel Holck Madsen 175c785705 Merge pull request #1722 from umbraco/temp-U4-9458
Changes IndexFieldPolicies to be a StaticFieldCollection which is a k…
2017-02-06 21:45:54 +01:00
Mikkel Holck Madsen 72aebd3ecf Merge branch 'dev-v7' into temp-U4-9458 2017-02-06 21:44:26 +01:00
Mikkel Holck Madsen 802cbb82c4 Merge pull request #1723 from umbraco/temp-U4-9448
U4-9448 Slave Front End server requires write access to database when…
2017-02-06 15:01:30 +01:00
Mikkel Holck Madsen 42f5507fe8 Merge pull request #1734 from umbraco/temp-U4-9492
U4-9492 Update to latest ImageProcessor
2017-02-06 14:39:01 +01:00
Sebastiaan Janssen 5a8e37f5b4 U4-9492 Update to latest ImageProcessor 2017-02-06 13:46:10 +01:00
Richard Soeteman a3c7b1fcfe Fixed issue U4-9494 2017-02-06 11:51:44 +01:00
Richard Soeteman 02785eea42 Fixed issue U4-9493 2017-02-06 11:26:58 +01:00
Shannon Deminick 3c7d3b2f63 Merge pull request #1724 from umbraco/temp-installer-url-update
Update NewInstallStep.cs
2017-02-06 19:42:23 +11:00
Stephan 49f3979d18 U4-9487 - optimize initialization of xml nodes 2017-02-03 19:28:43 +01:00
Shannon d598fdce54 Updates to latest Examine release 2017-02-03 12:03:56 +11:00
hartvig 32892cf5e0 Update NewInstallStep.cs 2017-02-02 10:05:14 +01:00
Shannon 32d757b13c Updates the ContentRepository to not fetch the published version info in the same query when returning a collection of documents, instead we fetch the published version information in a separate query 2017-02-02 17:05:33 +11:00
Shannon 882d5ec7f5 missing null check 2017-02-02 16:02:29 +11:00
Shannon ce69497a68 Update code to have zero breaking changes for old public APIs 2017-02-02 14:16:39 +11:00
Shannon 1d43cf491b U4-9448 Slave Front End server requires write access to database when master performs unpublish 2017-02-02 14:09:49 +11:00
Shannon 27283754c0 Changes IndexFieldPolicies to be a StaticFieldCollection which is a keyed collection instead of using Linq and iterating to find the values in critical code 2017-02-01 16:48:09 +11:00
Shannon 5b2212829c Small performance change which makes a fairly large impact when rebuilding very large indexes. 2017-02-01 13:02:22 +11:00
Morten Christensen 546209ddb5 Merge pull request #1716 from umbraco/temp-U4-9451
U4-9451 Package installation custom user control is not displayed aft…
2017-01-31 12:24:52 +01:00
Shannon 86b2dac231 Needed to change the 2nd query to use a new PagingSqlQuery object due to the way that < SQL 2012 formats it's paging query which is just not compatible with how we were parsing the queries for properties... i don't actually think it was working for a long tme. 2017-01-31 17:20:44 +11:00
Shannon 54bda6f12a U4-9451 Package installation custom user control is not displayed after a package is installed 2017-01-31 15:40:02 +11:00
Shannon 2f67a485aa Ensures that the sqlIds query is used for paging when processing members/media 2017-01-31 14:52:48 +11:00
Shannon f192f241cf U4-9450 Paged data queries return all property data in the entire database, not just for the paged subset 2017-01-31 13:48:47 +11:00
Sebastiaan Janssen 690af8e494 Merge pull request #1711 from umbraco/temp-U4-9444
U4-9444 SessionState is not available when rendering macro contents i…
2017-01-30 10:00:40 +01:00
Shannon 9d06ce53a4 U4-9444 SessionState is not available when rendering macro contents in the Rich Text Editor 2017-01-30 13:22:47 +11:00
Claus daa80a7bf6 Merge pull request #1709 from umbraco/temp-U4-9435-2
U4-9435 Fix breaking change made in U4-9312
2017-01-27 09:05:47 +01:00
Shannon 8152e7cb92 Doh, forgot a file change for tests - fixes build 2017-01-27 16:55:29 +11:00
Shannon Deminick adccb4161e Merge pull request #1707 from umbraco/temp-u4-9438
U4-9438 - fix nasty issue with reading properties in VersionableRepos…
2017-01-27 16:52:10 +11:00
Shannon 72a0187295 Writes unit test to confirm the correct property data iteration in GetPropertyCollection 2017-01-27 16:50:44 +11:00
Shannon 24d6287918 Adds string overloads just in case 2017-01-27 16:21:01 +11:00
Shannon 55900a3464 Updates MediaController and MediaTypeController to have the same method signature supporting both INT and GUID with a custom action selector to handle the ambiguity 2017-01-27 16:09:35 +11:00
Sebastiaan Janssen 36e9bb4d78 Merge pull request #1705 from umbraco/temp-v758-recyclebin-fix
Fixes U4-9437
2017-01-26 17:31:31 +01:00
Sebastiaan Janssen 639d04381e Bump version 2017-01-26 14:48:19 +01:00
Stephan 7f905bce0e U4-9438 - fix nasty issue with reading properties in VersionableRepositoryBase 2017-01-26 11:49:52 +01:00
Niels Hartvig 205ea22daa Fixes U4-9437 2017-01-25 17:34:34 +01:00
Shannon c7b505fd90 Updates the paging logic so that the ContentRepository uses 2x queries: A full query and a query for getting Ids. This is important because the query for fetching ids is used for paging and for getting property data and it can be much much faster than the full query which was previously used for both. There's not really any changes to the media/members respositories since their full queries don't have certain outer joins that make them run really slow 2017-01-24 16:04:26 +11:00
Sebastiaan Janssen c5f7ac18fd Merge pull request #1702 from umbraco/temp-U4-9427
Fixes: U4-9427 Unable to install local packages in various browsers
2017-01-23 16:38:11 +01:00
Sebastiaan Janssen 10b8fd9fb9 Merge pull request #1701 from umbraco/temp-U4-9420
U4-9420 Re-indexing Examine Indexes after Upgrade to 7.5.7 from 7.3.4 takes minutes when it used to take seconds
2017-01-23 16:18:45 +01:00
Mads Rasmussen 24c053d671 Fixes: U4-9427 Unable to install local packages in various browsers 2017-01-23 16:11:09 +01:00
Shannon 44fc8be49e Changes back to "path" default sorting but updates the UmbracoContentIndexer to ensure it's sorted by umbracoNode.id 2017-01-24 00:35:17 +11:00
Shannon 2a4e73c650 Reduced allocations for the Media/Content/Property Factories since we don't need to create new objects every time we want to map values.
Changes default sort order for paging from Path to umbracoNode.id since we have no index on Path and it doesn't make a lot of sense.
Fixes obsolete warnings for various Sql usages.
Reduces the amount of DeepClone calls required when looking up a content type for a content item, even though these are cached they are still deep cloned out of the cache.
Fixes the main issue of having nearly 100,000 rows of unsorted property data and then having to query those rows for every document being built, the re-iteration of these rows causes a lot of overhead and is unecessary, instead we ensure the property data set and the document data set is sorted by node id, then use a stored index to continue looking up the property data for the next content item found.
2017-01-23 00:40:24 +11:00
Sebastiaan Janssen f0a540eba2 Merge branch 'master-v7' into dev-v7 2017-01-20 09:55:50 +01:00
Sebastiaan Janssen 12922f8413 No need to warn if attribute is not present
Adds some logging about indexing being done.. the number of committed items seems to differ a little from the number of items in the index, not sure wh
2017-01-20 09:55:41 +01:00
Sebastiaan Janssen 85f054e3c1 No need to warn if attribute is not present
Adds some logging about indexing being done.. the number of committed items seems to differ a little from the number of items in the index, not sure wh
2017-01-20 09:54:59 +01:00
Sebastiaan Janssen 470bf86ec0 Merge branch 'dev-v7' into master-v7 2017-01-19 11:51:05 +01:00
Morten Christensen 9367f61952 Merge pull request #1697 from umbraco/temp-U4-9414
U4-9414 EntityContainer repository PerformGetAll query returns nothing
2017-01-19 11:49:19 +01:00
Sebastiaan Janssen 3cabd8c318 Merge branch 'dev-v7' into master-v7 2017-01-19 11:40:11 +01:00
Claus 4119380af5 U4-9414 EntityContainer repository PerformGetAll query returns nothing 2017-01-19 11:39:09 +01:00
Sebastiaan Janssen fc3b08e374 Merge pull request #1626 from Mark320/U4-9192
U4-9192 Add jQuery migrate for user controls used in tabs
2017-01-19 11:35:44 +01:00
Sebastiaan Janssen a57abce96e Manually applying https://github.com/umbraco/Umbraco-CMS/pull/1688 2017-01-19 11:30:14 +01:00
Sebastiaan Janssen b260f6c6de Merge pull request #1679 from umbraco/U-9333
Add support for GUIDs in media and content pickers
2017-01-19 11:25:34 +01:00
Shannon c96e017e65 Fixes PocoToSqlExpressionVisitor (oops), fixes examine tests by using the correct usings and NRT indexer/searcher to avoid waiting on thread timers 2017-01-19 14:00:33 +11:00
Shannon 4f596d20a9 refactors the fix for U4-9398 so it's not iterating multiple times over the descendants Enumerable 2017-01-19 13:13:49 +11:00
Shannon Deminick 343e462473 Merge pull request #1695 from mahgo/PerformIndexAll_paging_fix
Fixed UmbracoContentIndexer's PerformIndexAll SupportUnpublishedConte…
2017-01-19 13:10:30 +11:00
Shannon db414e8045 Fixes issue with implicitly non-published items getting into the index, fixes issue with sql syntax columns (i.e for mysql) for queries that query the cmsContentXml table 2017-01-19 12:51:56 +11:00
Sebastiaan Janssen 54e460cf5b U4-9392 Upgrade ImageProcessor with cache header and high CPU fix
U4-9393 Upgrade to Examine 0.1.80
2017-01-18 11:47:23 +01:00
Aaron Sturmfels ba64768d8d Fixed UmbracoContentIndexer's PerformIndexAll SupportUnpublishedContent = true paging breaking out of the loop early if filtering by node types. 2017-01-18 21:12:30 +11:00
Warren Buckley 6710ac8c85 Fix up camelCase method name to GetChildren 2017-01-18 09:43:21 +00:00
Warren Buckley 1f98b76da0 On app.authenticated JS event - we clear out LocalStorage values in case a different user is logging in 2017-01-18 09:34:02 +00:00
Sebastiaan Janssen 519eb9d641 Merge pull request #1694 from umbraco/temp-U4-9395
Temp u4 9395
2017-01-18 08:56:08 +01:00
Sebastiaan Janssen 931db6f0b1 U4-9395 - Handle errors with non-existing fields and invalide JSON data 2017-01-17 16:47:54 +01:00
Sebastiaan Janssen f24c56d1ba Merge pull request #1692 from umbraco/temp-U4-9395
U4-9395 When rebuilding content indexes that don't support unpublishe…
2017-01-17 11:45:17 +01:00
Sebastiaan Janssen 44608c3122 Merge branch 'dev-v7' into temp-U4-9395 2017-01-17 10:46:04 +01:00
Stephan 90f2fc4cd9 Merge pull request #1686 from umbraco/temp-U4-9388-2
Finds all other instances where a reader would be executed inside ano…
2017-01-17 08:18:54 +01:00
Shannon 24c7dee5d0 U4-9395 When rebuilding content indexes that don't support unpublished content and member indexes, use the cmsContentXml table as the data source 2017-01-17 17:57:49 +11:00
Shannon Deminick 12417c0c48 Merge pull request #1691 from umbraco/temp-UAASSCRUM-712
adds more logging for when indexes are rebuilt
2017-01-17 16:40:02 +11:00
Shannon Deminick d4919ff8c4 Merge pull request #1683 from umbraco/temp-U4-9371
U4-9371 Examine indexes inherited unpublished nodes on index rebuild …
2017-01-17 16:34:31 +11:00
Shannon b2acb97639 adds stopwatch timer to logging 2017-01-17 16:29:30 +11:00
Shannon 28debbda1b adds more logging for when indexes are rebuilt 2017-01-17 16:21:43 +11:00
Niels Hartvig c0a2a78f3d Fixes issue with entities not being returned from Keys (Guids) 2017-01-16 19:13:05 +01:00
Sebastiaan Janssen 3f7bfe0b3a Merge pull request #1685 from jamiepollock/U4-9387
U4-9387: Umbraco v7.5.x: Package Icon doesn't appear on Installed Packages screen when an icon URL is populated
2017-01-16 12:58:39 +01:00
Claus e0d281bb4b Merge pull request #1616 from abryukhov/patch-6
Update UI lang file for ru-ru
2017-01-16 11:26:23 +01:00
Sebastiaan Janssen b7d37af933 Bumps version 2017-01-16 11:04:28 +01:00
Claus 3f9cfbb422 Merge pull request #1642 from readingdancer/ChrisH-TypoFix
Small English grammar error.
2017-01-16 08:12:59 +01:00
Claus 2f3d926c08 Merge pull request #1638 from enkelmedia/dev-v7
Fixed english typo for modelsbuilder
2017-01-16 08:11:45 +01:00
Claus 594c3b8a85 Merge pull request #1630 from abjerner/patch-14
Typo (sorta)
2017-01-16 08:11:09 +01:00
Shannon Deminick 41244485b1 Merge pull request #1684 from umbraco/temp-U4-9377
U4-9377 It is possible to create/save entities with a blank names by …
2017-01-16 17:40:12 +11:00
Shannon ede342feba Finds all other instances where a reader would be executed inside another reader's loop and fixes 2017-01-16 16:38:23 +11:00
jamiepollock a15bdba5d7 View fix for icons not appearing in the installed package list 2017-01-15 18:26:20 -05:00
Claus 7d4a9998ca U4-9377 It is possible to create/save entities with a blank names by using the services directly
ensuring that when trying to save an entity with an empty name, directly through a service, an exception will be thrown.
2017-01-13 11:45:49 +01:00
Shannon bde439d102 fixes tests 2017-01-13 12:34:58 +11:00
Shannon ba501dbbb8 U4-9371 Examine indexes inherited unpublished nodes on index rebuild part 2 2017-01-13 10:43:34 +11:00
Niels Hartvig f75e0cf4ba Updates to PR based on Review 2017-01-10 12:47:00 +01:00
Sebastiaan Janssen 0dc3bff01a Merge branch 'dev-v7' into master-v7 2017-01-10 07:31:08 +01:00
Sebastiaan Janssen 77969960d9 Merge branch 'dev-v7' into master-v7
# Conflicts:
#	build/UmbracoVersion.txt
#	src/SolutionInfo.cs
#	src/Umbraco.Core/Configuration/UmbracoVersion.cs
#	src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
2017-01-09 16:26:07 +01:00
Niels Hartvig 1a9f336a59 Add support for GUIDs in media and content pickers 2017-01-09 14:45:40 +01:00
Sebastiaan Janssen 5bc66c5b7f Bumps the version 2016-12-07 14:18:27 +01:00
Chris Houston 90136c524c Small English grammar error. 2016-12-06 11:05:59 +00:00
Enkel Media 8d7f496f7c Merge pull request #1 from enkelmedia/enkelmedia-patch-1
Fixed simple typo for modelsbuilder
2016-11-30 11:18:25 +01:00
Enkel Media f62a9ed199 Fixed simple typo for modelsbuilder 2016-11-30 11:17:21 +01:00
Anders Bjerner bc8b8a69b8 Typo (sorta) 2016-11-21 10:26:08 +01:00
Mark320 9de3acfae2 U4-9192 Add jQuery migrate for user controls used in tabs that will improve backwards compatibility 2016-11-17 14:24:46 +00:00
Alexander Bryukhov 87814fa43c Update UI lang file for ru-ru
All latest 7.5.5 changes
2016-11-12 12:12:41 +07:00
Arnold Visser ca1f43d048 Merge pull request #1 from ArnoldV/Dutch-Language-Update
Dutch Languagefile Update
2016-09-06 10:53:49 +02:00
Arnold Visser e962a48138 Dutch Languagefile Update
This updates the Dutch language file with new/missing translations and fixes some wrong translations used by the grid.
2016-09-06 10:53:09 +02:00
122 changed files with 3895 additions and 1285 deletions
+5 -5
View File
@@ -28,17 +28,17 @@
<dependency id="MySql.Data" version="[6.9.8, 7.0.0)" />
<dependency id="xmlrpcnet" version="[2.5.0, 3.0.0)" />
<dependency id="ClientDependency" version="[1.9.2, 2.0.0)" />
<dependency id="ClientDependency-Mvc5" version="[1.8.0, 2.0.0)" />
<dependency id="ClientDependency-Mvc5" version="[1.8.0.0, 2.0.0)" />
<!-- AutoMapper can not be updated due to: https://github.com/AutoMapper/AutoMapper/issues/373#issuecomment-127644405 -->
<dependency id="AutoMapper" version="[3.3.1, 4.0.0)" />
<dependency id="Newtonsoft.Json" version="[6.0.8, 10.0.0)" />
<dependency id="Examine" version="[0.1.70, 1.0.0)" />
<dependency id="ImageProcessor" version="[2.5.1, 3.0.0)" />
<dependency id="ImageProcessor.Web" version="[4.7.2, 5.0.0)" />
<dependency id="Examine" version="[0.1.81, 1.0.0)" />
<dependency id="ImageProcessor" version="[2.5.2, 3.0.0)" />
<dependency id="ImageProcessor.Web" version="[4.8.2, 5.0.0)" />
<dependency id="semver" version="[1.1.2, 2.0.0)" />
<dependency id="UrlRewritingNet" version="[2.0.7, 3.0.0)" />
<!-- Markdown can not be updated due to: https://github.com/hey-red/markdownsharp/issues/71#issuecomment-233585487 -->
<dependency id="Markdown" version="[1.14.4, 2.0.0)" />
<dependency id="Markdown" version="[1.14.7, 2.0.0)" />
</dependencies>
</metadata>
<files>
+1
View File
@@ -45,6 +45,7 @@
<file src="tools\umbracoSettings.config.install.xdt" target="Content\config\umbracoSettings.config.install.xdt" />
<file src="tools\Views.Web.config.install.xdt" target="Views\Web.config.install.xdt" />
<file src="tools\processing.config.install.xdt" target="Content\Config\imageprocessor\processing.config.install.xdt" />
<file src="tools\cache.config.install.xdt" target="Content\Config\imageprocessor\cache.config.install.xdt" />
<file src="build\**" target="build" />
</files>
</package>
@@ -0,0 +1,6 @@
<?xml version="1.0"?>
<caching xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<caches>
<cache name="DiskCache" trimCache="false" xdt:Transform="SetAttributes(trimCache)" xdt:Locator="Match(name)" />
</caches>
</caching>
+1 -1
View File
@@ -1,2 +1,2 @@
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
7.5.7
7.5.10
+2 -2
View File
@@ -11,5 +11,5 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.5.7")]
[assembly: AssemblyInformationalVersion("7.5.7")]
[assembly: AssemblyFileVersion("7.5.10")]
[assembly: AssemblyInformationalVersion("7.5.10")]
+2 -1
View File
@@ -64,7 +64,8 @@ namespace Umbraco.Core.Cache
[UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
public const string ContentTypePropertiesCacheKey = "ContentType_PropertyTypes_Content:";
[UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
[Obsolete("No longer used and will be removed in v8")]
[EditorBrowsable(EditorBrowsableState.Never)]
public const string PropertyTypeCacheKey = "UmbracoPropertyTypeCache";
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration
{
public class UmbracoVersion
{
private static readonly Version Version = new Version("7.5.7");
private static readonly Version Version = new Version("7.5.10");
/// <summary>
/// Gets the current version of Umbraco.
+1 -1
View File
@@ -415,7 +415,7 @@ namespace Umbraco.Core
if (currentTry == 5)
{
throw new UmbracoStartupFailedException("Umbraco cannot start. A connection string is configured but the Umbraco cannot connect to the database.");
throw new UmbracoStartupFailedException("Umbraco cannot start. A connection string is configured but Umbraco cannot connect to the database.");
}
}
+4 -9
View File
@@ -203,15 +203,10 @@ namespace Umbraco.Core
var path = Path.Combine(GlobalSettings.FullpathToRoot, "App_Data", "Umbraco.sdf");
if (File.Exists(path) == false)
{
var engine = new SqlCeEngine(connectionString);
engine.CreateDatabase();
// SD: Pretty sure this should be in a using clause but i don't want to cause unknown side-effects here
// since it's been like this for quite some time
//using (var engine = new SqlCeEngine(connectionString))
//{
// engine.CreateDatabase();
//}
using (var engine = new SqlCeEngine(connectionString))
{
engine.CreateDatabase();
}
}
Initialize(providerName);
+1 -1
View File
@@ -4,7 +4,7 @@ namespace Umbraco.Core.Models
{
/// <summary>
/// Defines a ContentType, which Media is based on
/// </summary
/// </summary>
public interface IMediaType : IContentTypeComposition
{
@@ -13,6 +13,7 @@ namespace Umbraco.Core.Models.Identity
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
{
config.CreateMap<IUser, BackOfficeIdentityUser>()
.ForMember(user => user.LastLoginDateUtc, expression => expression.MapFrom(user => user.LastLoginDate.ToUniversalTime()))
.ForMember(user => user.Email, expression => expression.MapFrom(user => user.Email))
.ForMember(user => user.Id, expression => expression.MapFrom(user => user.Id))
.ForMember(user => user.LockoutEndDateUtc, expression => expression.MapFrom(user => user.IsLockedOut ? DateTime.MaxValue.ToUniversalTime() : (DateTime?)null))
@@ -24,12 +24,17 @@ namespace Umbraco.Core.Models.Identity
///
/// </summary>
public IdentityUser()
{
{
this.Claims = new List<TClaim>();
this.Roles = new List<TRole>();
this.Logins = new List<TLogin>();
}
/// <summary>
/// Last login date
/// </summary>
public virtual DateTime? LastLoginDateUtc { get; set; }
/// <summary>
/// Email
///
@@ -106,12 +106,13 @@ namespace Umbraco.Core.Models.PublishedContent
/// <param name="propertyTypeAlias">The property type alias.</param>
/// <param name="dataTypeDefinitionId">The datatype definition identifier.</param>
/// <param name="propertyEditorAlias">The property editor alias.</param>
/// <param name="initConverters">Generally used only for testing, in production this will always be true</param>
/// <remarks>
/// <para>The new published property type does not belong to a published content type.</para>
/// <para>The values of <paramref name="dataTypeDefinitionId"/> and <paramref name="propertyEditorAlias"/> are
/// assumed to be valid and consistent.</para>
/// </remarks>
internal PublishedPropertyType(string propertyTypeAlias, int dataTypeDefinitionId, string propertyEditorAlias)
internal PublishedPropertyType(string propertyTypeAlias, int dataTypeDefinitionId, string propertyEditorAlias, bool initConverters = true)
{
// ContentType
// - in unit tests, to be set by PublishedContentType when creating it
@@ -122,7 +123,8 @@ namespace Umbraco.Core.Models.PublishedContent
DataTypeId = dataTypeDefinitionId;
PropertyEditorAlias = propertyEditorAlias;
InitializeConverters();
if (initConverters)
InitializeConverters();
}
#endregion
@@ -27,15 +27,28 @@ namespace Umbraco.Core.Persistence.Factories
#region Implementation of IEntityFactory<IContent,DocumentDto>
public IContent BuildEntity(DocumentDto dto)
/// <summary>
/// Builds a IContent item from the dto(s) and content type
/// </summary>
/// <param name="dto">
/// This DTO can contain all of the information to build an IContent item, however in cases where multiple entities are being built,
/// a separate <see cref="DocumentPublishedReadOnlyDto"/> publishedDto entity will be supplied in place of the <see cref="DocumentDto"/>'s own
/// ResultColumn DocumentPublishedReadOnlyDto
/// </param>
/// <param name="contentType"></param>
/// <param name="publishedDto">
/// When querying for multiple content items the main DTO will not contain the ResultColumn DocumentPublishedReadOnlyDto and a separate publishedDto instance will be supplied
/// </param>
/// <returns></returns>
public static IContent BuildEntity(DocumentDto dto, IContentType contentType, DocumentPublishedReadOnlyDto publishedDto = null)
{
var content = new Content(dto.Text, dto.ContentVersionDto.ContentDto.NodeDto.ParentId, _contentType);
var content = new Content(dto.Text, dto.ContentVersionDto.ContentDto.NodeDto.ParentId, contentType);
try
{
content.DisableChangeTracking();
content.Id = _id;
content.Id = dto.NodeId;
content.Key = dto.ContentVersionDto.ContentDto.NodeDto.UniqueId;
content.Name = dto.Text;
content.NodeName = dto.ContentVersionDto.ContentDto.NodeDto.Text;
@@ -49,11 +62,16 @@ namespace Umbraco.Core.Persistence.Factories
content.Published = dto.Published;
content.CreateDate = dto.ContentVersionDto.ContentDto.NodeDto.CreateDate;
content.UpdateDate = dto.ContentVersionDto.VersionDate;
content.ExpireDate = dto.ExpiresDate.HasValue ? dto.ExpiresDate.Value : (DateTime?) null;
content.ReleaseDate = dto.ReleaseDate.HasValue ? dto.ReleaseDate.Value : (DateTime?) null;
content.ExpireDate = dto.ExpiresDate.HasValue ? dto.ExpiresDate.Value : (DateTime?)null;
content.ReleaseDate = dto.ReleaseDate.HasValue ? dto.ReleaseDate.Value : (DateTime?)null;
content.Version = dto.ContentVersionDto.VersionId;
content.PublishedState = dto.Published ? PublishedState.Published : PublishedState.Unpublished;
content.PublishedVersionGuid = dto.DocumentPublishedReadOnlyDto == null ? default(Guid) : dto.DocumentPublishedReadOnlyDto.VersionId;
//Check if the publishedDto has been supplied, if not the use the dto's own DocumentPublishedReadOnlyDto value
content.PublishedVersionGuid = publishedDto == null
? (dto.DocumentPublishedReadOnlyDto == null ? default(Guid) : dto.DocumentPublishedReadOnlyDto.VersionId)
: publishedDto.VersionId;
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
@@ -64,6 +82,13 @@ namespace Umbraco.Core.Persistence.Factories
{
content.EnableChangeTracking();
}
}
[Obsolete("Use the static BuildEntity instead so we don't have to allocate one of these objects everytime we want to map values")]
public IContent BuildEntity(DocumentDto dto)
{
return BuildEntity(dto, _contentType);
}
public DocumentDto BuildDto(IContent entity)
@@ -27,15 +27,15 @@ namespace Umbraco.Core.Persistence.Factories
#region Implementation of IEntityFactory<IMedia,ContentVersionDto>
public IMedia BuildEntity(ContentVersionDto dto)
public static IMedia BuildEntity(ContentVersionDto dto, IMediaType contentType)
{
var media = new Models.Media(dto.ContentDto.NodeDto.Text, dto.ContentDto.NodeDto.ParentId, _contentType);
var media = new Models.Media(dto.ContentDto.NodeDto.Text, dto.ContentDto.NodeDto.ParentId, contentType);
try
{
media.DisableChangeTracking();
media.Id = _id;
media.Id = dto.NodeId;
media.Key = dto.ContentDto.NodeDto.UniqueId;
media.Path = dto.ContentDto.NodeDto.Path;
media.CreatorId = dto.ContentDto.NodeDto.UserId.Value;
@@ -55,6 +55,13 @@ namespace Umbraco.Core.Persistence.Factories
{
media.EnableChangeTracking();
}
}
[Obsolete("Use the static BuildEntity instead so we don't have to allocate one of these objects everytime we want to map values")]
public IMedia BuildEntity(ContentVersionDto dto)
{
return BuildEntity(dto, _contentType);
}
public ContentVersionDto BuildDto(IMedia entity)
@@ -30,11 +30,11 @@ namespace Umbraco.Core.Persistence.Factories
_updateDate = updateDate;
}
public IEnumerable<Property> BuildEntity(PropertyDataDto[] dtos)
public static IEnumerable<Property> BuildEntity(IReadOnlyCollection<PropertyDataDto> dtos, PropertyType[] compositionTypeProperties, DateTime createDate, DateTime updateDate)
{
var properties = new List<Property>();
foreach (var propertyType in _compositionTypeProperties)
foreach (var propertyType in compositionTypeProperties)
{
var propertyDataDto = dtos.LastOrDefault(x => x.PropertyTypeId == propertyType.Id);
var property = propertyDataDto == null
@@ -47,8 +47,8 @@ namespace Umbraco.Core.Persistence.Factories
//on initial construction we don't want to have dirty properties tracked
property.DisableChangeTracking();
property.CreateDate = _createDate;
property.UpdateDate = _updateDate;
property.CreateDate = createDate;
property.UpdateDate = updateDate;
// http://issues.umbraco.org/issue/U4-1946
property.ResetDirtyProperties(false);
properties.Add(property);
@@ -57,12 +57,18 @@ namespace Umbraco.Core.Persistence.Factories
{
property.EnableChangeTracking();
}
}
return properties;
}
[Obsolete("Use the static method instead, there's no reason to allocate one of these classes everytime we want to map values")]
public IEnumerable<Property> BuildEntity(PropertyDataDto[] dtos)
{
return BuildEntity(dtos, _compositionTypeProperties, _createDate, _updateDate);
}
public IEnumerable<PropertyDataDto> BuildDto(IEnumerable<Property> properties)
{
var propertyDataDtos = new List<PropertyDataDto>();
@@ -20,6 +20,7 @@ namespace Umbraco.Core.Persistence.Querying
_mapper = mapper;
}
[Obsolete("Use the overload the specifies a SqlSyntaxProvider")]
public ModelToSqlExpressionVisitor()
: this(SqlSyntaxContext.SqlSyntaxProvider, MappingResolver.Current.ResolveMapperByType(typeof(T)))
{ }
@@ -18,14 +18,13 @@ namespace Umbraco.Core.Persistence.Querying
public PocoToSqlExpressionVisitor(ISqlSyntaxProvider syntaxProvider)
: base(syntaxProvider)
{
_pd = new Database.PocoData(typeof(T));
}
[Obsolete("Use the overload the specifies a SqlSyntaxProvider")]
public PocoToSqlExpressionVisitor()
: base(SqlSyntaxContext.SqlSyntaxProvider)
{
_pd = new Database.PocoData(typeof(T));
: this(SqlSyntaxContext.SqlSyntaxProvider)
{
}
protected override string VisitMemberAccess(MemberExpression m)
@@ -0,0 +1,31 @@
namespace Umbraco.Core.Persistence.Repositories
{
internal enum BaseQueryType
{
/// <summary>
/// A query to return all information for a single item
/// </summary>
/// <remarks>
/// In some cases this will be the same as <see cref="FullMultiple"/>
/// </remarks>
FullSingle,
/// <summary>
/// A query to return all information for multiple items
/// </summary>
/// <remarks>
/// In some cases this will be the same as <see cref="FullSingle"/>
/// </remarks>
FullMultiple,
/// <summary>
/// A query to return the ids for items
/// </summary>
Ids,
/// <summary>
/// A query to return the count for items
/// </summary>
Count
}
}
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Xml;
@@ -53,9 +54,9 @@ namespace Umbraco.Core.Persistence.Repositories
protected override IContent PerformGet(int id)
{
var sql = GetBaseQuery(false)
var sql = GetBaseQuery(BaseQueryType.FullSingle)
.Where(GetBaseWhereClause(), new { Id = id })
.Where<DocumentDto>(x => x.Newest)
.Where<DocumentDto>(x => x.Newest, SqlSyntax)
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
var dto = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto, DocumentPublishedReadOnlyDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
@@ -70,63 +71,102 @@ namespace Umbraco.Core.Persistence.Repositories
protected override IEnumerable<IContent> PerformGetAll(params int[] ids)
{
var sql = GetBaseQuery(false);
if (ids.Any())
Func<Sql, Sql> translate = s =>
{
sql.Where("umbracoNode.id in (@ids)", new { ids });
}
if (ids.Any())
{
s.Where("umbracoNode.id in (@ids)", new { ids });
}
//we only want the newest ones with this method
s.Where<DocumentDto>(x => x.Newest, SqlSyntax);
return s;
};
var sqlBaseFull = GetBaseQuery(BaseQueryType.FullMultiple);
var sqlBaseIds = GetBaseQuery(BaseQueryType.Ids);
//we only want the newest ones with this method
sql.Where<DocumentDto>(x => x.Newest);
return ProcessQuery(sql);
return ProcessQuery(translate(sqlBaseFull), new PagingSqlQuery(translate(sqlBaseIds)));
}
protected override IEnumerable<IContent> PerformGetByQuery(IQuery<IContent> query)
{
var sqlClause = GetBaseQuery(false);
var translator = new SqlTranslator<IContent>(sqlClause, query);
var sql = translator.Translate()
.Where<DocumentDto>(x => x.Newest)
.OrderByDescending<ContentVersionDto>(x => x.VersionDate)
.OrderBy<NodeDto>(x => x.SortOrder);
var sqlBaseFull = GetBaseQuery(BaseQueryType.FullMultiple);
var sqlBaseIds = GetBaseQuery(BaseQueryType.Ids);
return ProcessQuery(sql);
Func<SqlTranslator<IContent>, Sql> translate = (translator) =>
{
return translator.Translate()
.Where<DocumentDto>(x => x.Newest, SqlSyntax)
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax)
.OrderBy<NodeDto>(x => x.SortOrder, SqlSyntax);
};
var translatorFull = new SqlTranslator<IContent>(sqlBaseFull, query);
var translatorIds = new SqlTranslator<IContent>(sqlBaseIds, query);
return ProcessQuery(translate(translatorFull), new PagingSqlQuery(translate(translatorIds)));
}
#endregion
#region Overrides of PetaPocoRepositoryBase<IContent>
protected override Sql GetBaseQuery(bool isCount)
/// <summary>
/// Returns the base query to return Content
/// </summary>
/// <param name="queryType"></param>
/// <returns></returns>
/// <remarks>
/// Content queries will differ depending on what needs to be returned:
/// * FullSingle: When querying for a single document, this will include the Outer join to fetch the content item's published version info
/// * FullMultiple: When querying for multiple documents, this will exclude the Outer join to fetch the content item's published version info - this info would need to be fetched separately
/// * Ids: This would essentially be the same as FullMultiple however the columns specified will only return the Ids for the documents
/// * Count: A query to return the count for documents
/// </remarks>
protected override Sql GetBaseQuery(BaseQueryType queryType)
{
var sqlx = string.Format("LEFT OUTER JOIN {0} {1} ON ({1}.{2}={0}.{2} AND {1}.{3}=1)",
var sql = new Sql();
sql.Select(queryType == BaseQueryType.Count ? "COUNT(*)" : (queryType == BaseQueryType.Ids ? "cmsDocument.nodeId" : "*"))
.From<DocumentDto>(SqlSyntax)
.InnerJoin<ContentVersionDto>(SqlSyntax)
.On<DocumentDto, ContentVersionDto>(SqlSyntax, left => left.VersionId, right => right.VersionId)
.InnerJoin<ContentDto>(SqlSyntax)
.On<ContentVersionDto, ContentDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
.InnerJoin<NodeDto>(SqlSyntax)
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId);
if (queryType == BaseQueryType.FullSingle)
{
//The only reason we apply this left outer join is to be able to pull back the DocumentPublishedReadOnlyDto
//information with the entire data set, so basically this will get both the latest document and also it's published
//version if it has one. When performing a count or when retrieving Ids like in paging, this is unecessary
//and causes huge performance overhead for the SQL server, especially when sorting the result.
//We also don't include this outer join when querying for multiple entities since it is much faster to fetch this information
//in a separate query. For a single entity this is ok.
var sqlx = string.Format("LEFT OUTER JOIN {0} {1} ON ({1}.{2}={0}.{2} AND {1}.{3}=1)",
SqlSyntax.GetQuotedTableName("cmsDocument"),
SqlSyntax.GetQuotedTableName("cmsDocument2"),
SqlSyntax.GetQuotedColumnName("nodeId"),
SqlSyntax.GetQuotedColumnName("published"));
var sql = new Sql();
sql.Select(isCount ? "COUNT(*)" : "*")
.From<DocumentDto>()
.InnerJoin<ContentVersionDto>()
.On<DocumentDto, ContentVersionDto>(left => left.VersionId, right => right.VersionId)
.InnerJoin<ContentDto>()
.On<ContentVersionDto, ContentDto>(left => left.NodeId, right => right.NodeId)
.InnerJoin<NodeDto>()
.On<ContentDto, NodeDto>(left => left.NodeId, right => right.NodeId)
// cannot do this because PetaPoco does not know how to alias the table
//.LeftOuterJoin<DocumentPublishedReadOnlyDto>()
//.On<DocumentDto, DocumentPublishedReadOnlyDto>(left => left.NodeId, right => right.NodeId)
// so have to rely on writing our own SQL
.Append(sqlx/*, new { @published = true }*/)
sql.Append(sqlx /*, new { @published = true }*/);
}
sql.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId, SqlSyntax);
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
return sql;
}
protected override Sql GetBaseQuery(bool isCount)
{
return GetBaseQuery(isCount ? BaseQueryType.Count : BaseQueryType.FullSingle);
}
protected override string GetBaseWhereClause()
{
return "umbracoNode.id = @Id";
@@ -173,20 +213,32 @@ namespace Umbraco.Core.Persistence.Repositories
// not bring much safety - so this reverts to updating each record individually,
// and it may be slower in the end, but should be more resilient.
var baseId = 0;
var contentTypeIdsA = contentTypeIds == null ? new int[0] : contentTypeIds.ToArray();
Func<int, Sql, Sql> translate = (bId, sql) =>
{
if (contentTypeIdsA.Length > 0)
{
sql.WhereIn<ContentDto>(x => x.ContentTypeId, contentTypeIdsA, SqlSyntax);
}
sql
.Where<NodeDto>(x => x.NodeId > bId && x.Trashed == false, SqlSyntax)
.Where<DocumentDto>(x => x.Published, SqlSyntax)
.OrderBy<NodeDto>(x => x.NodeId, SqlSyntax);
return sql;
};
var baseId = 0;
while (true)
{
// get the next group of nodes
var query = GetBaseQuery(false);
if (contentTypeIdsA.Length > 0)
query = query
.WhereIn<ContentDto>(x => x.ContentTypeId, contentTypeIdsA, SqlSyntax);
query = query
.Where<NodeDto>(x => x.NodeId > baseId && x.Trashed == false)
.Where<DocumentDto>(x => x.Published)
.OrderBy<NodeDto>(x => x.NodeId, SqlSyntax);
var xmlItems = ProcessQuery(SqlSyntax.SelectTop(query, groupSize))
var sqlFull = translate(baseId, GetBaseQuery(BaseQueryType.FullMultiple));
var sqlIds = translate(baseId, GetBaseQuery(BaseQueryType.Ids));
var xmlItems = ProcessQuery(SqlSyntax.SelectTop(sqlFull, groupSize), new PagingSqlQuery(SqlSyntax.SelectTop(sqlIds, groupSize)))
.Select(x => new ContentXmlDto { NodeId = x.Id, Xml = serializer(x).ToString() })
.ToList();
@@ -206,23 +258,29 @@ namespace Umbraco.Core.Persistence.Repositories
Logger.Error<MediaRepository>("Could not rebuild XML for nodeId=" + xmlItem.NodeId, e);
}
}
baseId = xmlItems.Last().NodeId;
baseId = xmlItems[xmlItems.Count - 1].NodeId;
}
}
public override IEnumerable<IContent> GetAllVersions(int id)
{
var sql = GetBaseQuery(false)
.Where(GetBaseWhereClause(), new { Id = id })
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
return ProcessQuery(sql, true);
Func<Sql, Sql> translate = s =>
{
return s.Where(GetBaseWhereClause(), new {Id = id})
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
};
var sqlFull = translate(GetBaseQuery(BaseQueryType.FullMultiple));
var sqlIds = translate(GetBaseQuery(BaseQueryType.Ids));
return ProcessQuery(sqlFull, new PagingSqlQuery(sqlIds), true);
}
public override IContent GetByVersion(Guid versionId)
{
var sql = GetBaseQuery(false);
var sql = GetBaseQuery(BaseQueryType.FullSingle);
sql.Where("cmsContentVersion.VersionId = @VersionId", new { VersionId = versionId });
sql.OrderByDescending<ContentVersionDto>(x => x.VersionDate);
sql.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
var dto = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto, DocumentPublishedReadOnlyDto>(sql).FirstOrDefault();
@@ -238,10 +296,10 @@ namespace Umbraco.Core.Persistence.Repositories
{
var sql = new Sql()
.Select("*")
.From<DocumentDto>()
.InnerJoin<ContentVersionDto>().On<ContentVersionDto, DocumentDto>(left => left.VersionId, right => right.VersionId)
.Where<ContentVersionDto>(x => x.VersionId == versionId)
.Where<DocumentDto>(x => x.Newest != true);
.From<DocumentDto>(SqlSyntax)
.InnerJoin<ContentVersionDto>(SqlSyntax).On<ContentVersionDto, DocumentDto>(SqlSyntax, left => left.VersionId, right => right.VersionId)
.Where<ContentVersionDto>(x => x.VersionId == versionId, SqlSyntax)
.Where<DocumentDto>(x => x.Newest != true, SqlSyntax);
var dto = Database.Fetch<DocumentDto, ContentVersionDto>(sql).FirstOrDefault();
if (dto == null) return;
@@ -616,21 +674,27 @@ namespace Umbraco.Core.Persistence.Repositories
public IEnumerable<IContent> GetByPublishedVersion(IQuery<IContent> query)
{
Func<SqlTranslator<IContent>, Sql> translate = t =>
{
return t.Translate()
.Where<DocumentDto>(x => x.Published, SqlSyntax)
.OrderBy<NodeDto>(x => x.Level, SqlSyntax)
.OrderBy<NodeDto>(x => x.SortOrder, SqlSyntax);
};
// we WANT to return contents in top-down order, ie parents should come before children
// ideal would be pure xml "document order" which can be achieved with:
// ORDER BY substring(path, 1, len(path) - charindex(',', reverse(path))), sortOrder
// but that's probably an overkill - sorting by level,sortOrder should be enough
var sqlClause = GetBaseQuery(false);
var translator = new SqlTranslator<IContent>(sqlClause, query);
var sql = translator.Translate()
.Where<DocumentDto>(x => x.Published)
.OrderBy<NodeDto>(x => x.Level, SqlSyntax)
.OrderBy<NodeDto>(x => x.SortOrder, SqlSyntax);
var sqlFull = GetBaseQuery(BaseQueryType.FullMultiple);
var translatorFull = new SqlTranslator<IContent>(sqlFull, query);
var sqlIds = GetBaseQuery(BaseQueryType.Ids);
var translatorIds = new SqlTranslator<IContent>(sqlIds, query);
return ProcessQuery(sql, true);
return ProcessQuery(translate(translatorFull), new PagingSqlQuery(translate(translatorIds)), true);
}
/// <summary>
/// This builds the Xml document used for the XML cache
/// </summary>
@@ -662,10 +726,14 @@ namespace Umbraco.Core.Persistence.Repositories
parent.Attributes.Append(pIdAtt);
xmlDoc.AppendChild(parent);
const string sql = @"select umbracoNode.id, umbracoNode.parentID, umbracoNode.sortOrder, cmsContentXml.xml, umbracoNode.level from umbracoNode
//Ensure that only nodes that have published versions are selected
var sql = string.Format(@"select umbracoNode.id, umbracoNode.parentID, umbracoNode.sortOrder, cmsContentXml.{0}, umbracoNode.{1} from umbracoNode
inner join cmsContentXml on cmsContentXml.nodeId = umbracoNode.id and umbracoNode.nodeObjectType = @type
where umbracoNode.id in (select cmsDocument.nodeId from cmsDocument where cmsDocument.published = 1)
order by umbracoNode.level, umbracoNode.parentID, umbracoNode.sortOrder";
order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
SqlSyntax.GetQuotedColumnName("xml"),
SqlSyntax.GetQuotedColumnName("level"),
SqlSyntax.GetQuotedColumnName("level"));
XmlElement last = null;
@@ -716,13 +784,8 @@ order by umbracoNode.level, umbracoNode.parentID, umbracoNode.sortOrder";
public void ClearPublished(IContent content)
{
// race cond!
var documentDtos = Database.Fetch<DocumentDto>("WHERE nodeId=@id AND published=@published", new { id = content.Id, published = true });
foreach (var documentDto in documentDtos)
{
documentDto.Published = false;
Database.Update(documentDto);
}
var sql = "UPDATE cmsDocument SET published=0 WHERE nodeId=@id AND published=1";
Database.Execute(sql, new {id = content.Id});
}
/// <summary>
@@ -802,9 +865,9 @@ order by umbracoNode.level, umbracoNode.parentID, umbracoNode.sortOrder";
Func<Tuple<string, object[]>> filterCallback = () => new Tuple<string, object[]>(filterSql.SQL, filterSql.Arguments);
return GetPagedResultsByQuery<DocumentDto, Content>(query, pageIndex, pageSize, out totalRecords,
return GetPagedResultsByQuery<DocumentDto>(query, pageIndex, pageSize, out totalRecords,
new Tuple<string, string>("cmsDocument", "nodeId"),
sql => ProcessQuery(sql), orderBy, orderDirection, orderBySystemField,
(sqlFull, pagingSqlQuery) => ProcessQuery(sqlFull, pagingSqlQuery), orderBy, orderDirection, orderBySystemField,
filterCallback);
}
@@ -834,20 +897,73 @@ order by umbracoNode.level, umbracoNode.parentID, umbracoNode.sortOrder";
return base.GetDatabaseFieldNameForOrderBy(orderBy);
}
private IEnumerable<IContent> ProcessQuery(Sql sql, bool withCache = false)
/// <summary>
/// This is the underlying method that processes most queries for this repository
/// </summary>
/// <param name="sqlFull">
/// The FullMultiple SQL without the outer join to return all data required to create an IContent excluding it's published state data which this will query separately
/// </param>
/// <param name="pagingSqlQuery">
/// The Id SQL without the outer join to just return all document ids - used to process the properties for the content item
/// </param>
/// <param name="withCache"></param>
/// <returns></returns>
private IEnumerable<IContent> ProcessQuery(Sql sqlFull, PagingSqlQuery pagingSqlQuery, bool withCache = false)
{
// fetch returns a list so it's ok to iterate it in this method
var dtos = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto, DocumentPublishedReadOnlyDto>(sql);
var dtos = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto>(sqlFull);
if (dtos.Count == 0) return Enumerable.Empty<IContent>();
//Go and get all of the published version data separately for this data, this is because when we are querying
//for multiple content items we don't include the outer join to fetch this data in the same query because
//it is insanely slow. Instead we just fetch the published version data separately in one query.
//we need to parse the original SQL statement and reduce the columns to just cmsDocument.nodeId so that we can use
// the statement to go get the published data for all of the items by using an inner join
var parsedOriginalSql = "SELECT cmsDocument.nodeId " + sqlFull.SQL.Substring(sqlFull.SQL.IndexOf("FROM", StringComparison.Ordinal));
//now remove everything from an Orderby clause and beyond
if (parsedOriginalSql.InvariantContains("ORDER BY "))
{
parsedOriginalSql = parsedOriginalSql.Substring(0, parsedOriginalSql.LastIndexOf("ORDER BY ", StringComparison.Ordinal));
}
var publishedSql = new Sql(@"SELECT *
FROM cmsDocument AS doc2
INNER JOIN
(" + parsedOriginalSql + @") as docData
ON doc2.nodeId = docData.nodeId
WHERE doc2.published = 1
ORDER BY doc2.nodeId
", sqlFull.Arguments);
//go and get the published version data, we do a Query here and not a Fetch so we are
//not allocating a whole list to memory just to allocate another list in memory since
//we are assigning this data to a keyed collection for fast lookup below
var publishedData = Database.Query<DocumentPublishedReadOnlyDto>(publishedSql);
var publishedDataCollection = new DocumentPublishedReadOnlyDtoCollection();
foreach (var publishedDto in publishedData)
{
//double check that there's no corrupt db data, there should only be a single published item
if (publishedDataCollection.Contains(publishedDto.NodeId) == false)
publishedDataCollection.Add(publishedDto);
}
var content = new IContent[dtos.Count];
var defs = new List<DocumentDefinition>();
var templateIds = new List<int>();
//track the looked up content types, even though the content types are cached
// they still need to be deep cloned out of the cache and we don't want to add
// the overhead of deep cloning them on every item in this loop
var contentTypes = new Dictionary<int, IContentType>();
for (var i = 0; i < dtos.Count; i++)
{
var dto = dtos[i];
DocumentPublishedReadOnlyDto publishedDto;
publishedDataCollection.TryGetValue(dto.NodeId, out publishedDto);
// if the cache contains the published version, use it
if (withCache)
@@ -863,9 +979,19 @@ order by umbracoNode.level, umbracoNode.parentID, umbracoNode.sortOrder";
// else, need to fetch from the database
// content type repository is full-cache so OK to get each one independently
var contentType = _contentTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId);
var factory = new ContentFactory(contentType, NodeObjectTypeId, dto.NodeId);
content[i] = factory.BuildEntity(dto);
IContentType contentType;
if (contentTypes.ContainsKey(dto.ContentVersionDto.ContentDto.ContentTypeId))
{
contentType = contentTypes[dto.ContentVersionDto.ContentDto.ContentTypeId];
}
else
{
contentType = _contentTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId);
contentTypes[dto.ContentVersionDto.ContentDto.ContentTypeId] = contentType;
}
content[i] = ContentFactory.BuildEntity(dto, contentType, publishedDto);
// need template
if (dto.TemplateId.HasValue && dto.TemplateId.Value > 0)
@@ -886,7 +1012,7 @@ order by umbracoNode.level, umbracoNode.parentID, umbracoNode.sortOrder";
.ToDictionary(x => x.Id, x => x);
// load all properties for all documents from database in 1 query
var propertyData = GetPropertyCollection(sql, defs);
var propertyData = GetPropertyCollection(pagingSqlQuery, defs);
// assign
var dtoIndex = 0;
@@ -906,7 +1032,7 @@ order by umbracoNode.level, umbracoNode.parentID, umbracoNode.sortOrder";
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
((Entity) cc).ResetDirtyProperties(false);
cc.ResetDirtyProperties(false);
}
return content;
@@ -923,8 +1049,7 @@ order by umbracoNode.level, umbracoNode.parentID, umbracoNode.sortOrder";
{
var contentType = _contentTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId);
var factory = new ContentFactory(contentType, NodeObjectTypeId, dto.NodeId);
var content = factory.BuildEntity(dto);
var content = ContentFactory.BuildEntity(dto, contentType);
//Check if template id is set on DocumentDto, and get ITemplate if it is.
if (dto.TemplateId.HasValue && dto.TemplateId.Value > 0)
@@ -975,7 +1100,7 @@ order by umbracoNode.level, umbracoNode.parentID, umbracoNode.sortOrder";
return currentName;
}
/// <summary>
/// Dispose disposable properties
/// </summary>
@@ -990,5 +1115,26 @@ order by umbracoNode.level, umbracoNode.parentID, umbracoNode.sortOrder";
_contentPreviewRepository.Dispose();
_contentXmlRepository.Dispose();
}
/// <summary>
/// A keyed collection for fast lookup when retrieving a separate list of published version data
/// </summary>
private class DocumentPublishedReadOnlyDtoCollection : KeyedCollection<int, DocumentPublishedReadOnlyDto>
{
protected override int GetKeyForItem(DocumentPublishedReadOnlyDto item)
{
return item.NodeId;
}
public bool TryGetValue(int key, out DocumentPublishedReadOnlyDto val)
{
if (Dictionary == null)
{
val = null;
return false;
}
return Dictionary.TryGetValue(key, out val);
}
}
}
}
@@ -62,17 +62,27 @@ namespace Umbraco.Core.Persistence.Repositories
protected override IEnumerable<EntityContainer> PerformGetAll(params int[] ids)
{
//we need to batch these in groups of 2000 so we don't exceed the max 2100 limit
return ids.InGroupsOf(2000).SelectMany(@group =>
if (ids.Any())
{
//we need to batch these in groups of 2000 so we don't exceed the max 2100 limit
return ids.InGroupsOf(2000).SelectMany(@group =>
{
var sql = GetBaseQuery(false)
.Where("nodeObjectType=@umbracoObjectTypeId", new {umbracoObjectTypeId = NodeObjectTypeId})
.Where(string.Format("{0} IN (@ids)", SqlSyntax.GetQuotedColumnName("id")), new {ids = @group});
sql.OrderBy<NodeDto>(x => x.Level, SqlSyntax);
return Database.Fetch<NodeDto>(sql).Select(CreateEntity);
});
}
else
{
var sql = GetBaseQuery(false)
.Where("nodeObjectType=@umbracoObjectTypeId", new { umbracoObjectTypeId = NodeObjectTypeId })
.Where(string.Format("{0} IN (@ids)", SqlSyntax.GetQuotedColumnName("id")), new { ids = @group });
.Where("nodeObjectType=@umbracoObjectTypeId", new {umbracoObjectTypeId = NodeObjectTypeId});
sql.OrderBy<NodeDto>(x => x.Level, SqlSyntax);
return Database.Fetch<NodeDto>(sql).Select(CreateEntity);
});
}
}
protected override IEnumerable<EntityContainer> PerformGetByQuery(IQuery<EntityContainer> query)
@@ -96,5 +96,6 @@ namespace Umbraco.Core.Persistence.Repositories
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
IEnumerable<IContent> GetPagedResultsByQuery(IQuery<IContent> query, long pageIndex, int pageSize, out long totalRecords,
string orderBy, Direction orderDirection, bool orderBySystemField, IQuery<IContent> filter = null);
}
}
@@ -38,15 +38,6 @@ namespace Umbraco.Core.Persistence.Repositories
/// <returns>An Enumerable list of <see cref="IMedia"/> objects</returns>
IEnumerable<IMedia> GetPagedResultsByQuery(IQuery<IMedia> query, long pageIndex, int pageSize, out long totalRecords,
string orderBy, Direction orderDirection, bool orderBySystemField, string filter = "");
/// <summary>
/// Gets paged media descendants as XML by path
/// </summary>
/// <param name="path">Path starts with</param>
/// <param name="pageIndex">Page number</param>
/// <param name="pageSize">Page size</param>
/// <param name="totalRecords">Total records the query would return without paging</param>
/// <returns>A paged enumerable of XML entries of media items</returns>
IEnumerable<XElement> GetPagedXmlEntriesByPath(string path, long pageIndex, int pageSize, out long totalRecords);
}
}
@@ -73,6 +73,6 @@ namespace Umbraco.Core.Persistence.Repositories
/// <param name="content"></param>
/// <param name="xml"></param>
void AddOrUpdatePreviewXml(IMember content, Func<IMember, XElement> xml);
}
}
@@ -67,5 +67,16 @@ namespace Umbraco.Core.Persistence.Repositories
/// <param name="id">Id of the <see cref="TEntity"/> object to delete versions from</param>
/// <param name="versionDate">Latest version date</param>
void DeleteVersions(int id, DateTime versionDate);
/// <summary>
/// Gets paged content descendants as XML by path
/// </summary>
/// <param name="path">Path starts with</param>
/// <param name="pageIndex">Page number</param>
/// <param name="pageSize">Page size</param>
/// <param name="orderBy"></param>
/// <param name="totalRecords">Total records the query would return without paging</param>
/// <returns>A paged enumerable of XML entries of content items</returns>
IEnumerable<XElement> GetPagedXmlEntriesByPath(string path, long pageIndex, int pageSize, string[] orderBy, out long totalRecords);
}
}
@@ -68,7 +68,7 @@ namespace Umbraco.Core.Persistence.Repositories
sql.Where("umbracoNode.id in (@ids)", new { ids = ids });
}
return ProcessQuery(sql);
return ProcessQuery(sql, new PagingSqlQuery(sql));
}
protected override IEnumerable<IMedia> PerformGetByQuery(IQuery<IMedia> query)
@@ -76,26 +76,31 @@ namespace Umbraco.Core.Persistence.Repositories
var sqlClause = GetBaseQuery(false);
var translator = new SqlTranslator<IMedia>(sqlClause, query);
var sql = translator.Translate()
.OrderBy<NodeDto>(x => x.SortOrder);
.OrderBy<NodeDto>(x => x.SortOrder, SqlSyntax);
return ProcessQuery(sql);
return ProcessQuery(sql, new PagingSqlQuery(sql));
}
#endregion
#region Overrides of PetaPocoRepositoryBase<int,IMedia>
protected override Sql GetBaseQuery(BaseQueryType queryType)
{
var sql = new Sql();
sql.Select(queryType == BaseQueryType.Count ? "COUNT(*)" : (queryType == BaseQueryType.Ids ? "cmsContentVersion.contentId" : "*"))
.From<ContentVersionDto>(SqlSyntax)
.InnerJoin<ContentDto>(SqlSyntax)
.On<ContentVersionDto, ContentDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
.InnerJoin<NodeDto>(SqlSyntax)
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId, SqlSyntax)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId, SqlSyntax);
return sql;
}
protected override Sql GetBaseQuery(bool isCount)
{
var sql = new Sql();
sql.Select(isCount ? "COUNT(*)" : "*")
.From<ContentVersionDto>()
.InnerJoin<ContentDto>()
.On<ContentVersionDto, ContentDto>(left => left.NodeId, right => right.NodeId)
.InnerJoin<NodeDto>()
.On<ContentDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
return sql;
return GetBaseQuery(isCount ? BaseQueryType.Count : BaseQueryType.FullSingle);
}
protected override string GetBaseWhereClause()
@@ -138,16 +143,32 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = GetBaseQuery(false)
.Where(GetBaseWhereClause(), new { Id = id })
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
return ProcessQuery(sql, true);
return ProcessQuery(sql, new PagingSqlQuery(sql), true);
}
private IEnumerable<IMedia> ProcessQuery(Sql sql, bool withCache = false)
/// <summary>
/// This is the underlying method that processes most queries for this repository
/// </summary>
/// <param name="sqlFull">
/// The full SQL to select all media data
/// </param>
/// <param name="pagingSqlQuery">
/// The Id SQL to just return all media ids - used to process the properties for the media item
/// </param>
/// <param name="withCache"></param>
/// <returns></returns>
private IEnumerable<IMedia> ProcessQuery(Sql sqlFull, PagingSqlQuery pagingSqlQuery, bool withCache = false)
{
// fetch returns a list so it's ok to iterate it in this method
var dtos = Database.Fetch<ContentVersionDto, ContentDto, NodeDto>(sql);
var dtos = Database.Fetch<ContentVersionDto, ContentDto, NodeDto>(sqlFull);
var content = new IMedia[dtos.Count];
var defs = new List<DocumentDefinition>();
//track the looked up content types, even though the content types are cached
// they still need to be deep cloned out of the cache and we don't want to add
// the overhead of deep cloning them on every item in this loop
var contentTypes = new Dictionary<int, IMediaType>();
for (var i = 0; i < dtos.Count; i++)
{
var dto = dtos[i];
@@ -165,9 +186,19 @@ namespace Umbraco.Core.Persistence.Repositories
// else, need to fetch from the database
// content type repository is full-cache so OK to get each one independently
var contentType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId);
var factory = new MediaFactory(contentType, NodeObjectTypeId, dto.NodeId);
content[i] = factory.BuildEntity(dto);
IMediaType contentType;
if (contentTypes.ContainsKey(dto.ContentDto.ContentTypeId))
{
contentType = contentTypes[dto.ContentDto.ContentTypeId];
}
else
{
contentType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId);
contentTypes[dto.ContentDto.ContentTypeId] = contentType;
}
content[i] = MediaFactory.BuildEntity(dto, contentType);
// need properties
defs.Add(new DocumentDefinition(
@@ -180,7 +211,7 @@ namespace Umbraco.Core.Persistence.Repositories
}
// load all properties for all documents from database in 1 query
var propertyData = GetPropertyCollection(sql, defs);
var propertyData = GetPropertyCollection(pagingSqlQuery, defs);
// assign
var dtoIndex = 0;
@@ -195,7 +226,7 @@ namespace Umbraco.Core.Persistence.Repositories
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
((Entity) cc).ResetDirtyProperties(false);
cc.ResetDirtyProperties(false);
}
return content;
@@ -205,26 +236,16 @@ namespace Umbraco.Core.Persistence.Repositories
{
var sql = GetBaseQuery(false);
sql.Where("cmsContentVersion.VersionId = @VersionId", new { VersionId = versionId });
sql.OrderByDescending<ContentVersionDto>(x => x.VersionDate);
sql.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
var dto = Database.Fetch<ContentVersionDto, ContentDto, NodeDto>(sql).FirstOrDefault();
if (dto == null)
return null;
var mediaType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId);
var content = CreateMediaFromDto(dto, versionId, sql);
var factory = new MediaFactory(mediaType, NodeObjectTypeId, dto.NodeId);
var media = factory.BuildEntity(dto);
var properties = GetPropertyCollection(sql, new[] { new DocumentDefinition(dto.NodeId, dto.VersionId, media.UpdateDate, media.CreateDate, mediaType) });
media.Properties = properties[dto.NodeId];
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
((Entity)media).ResetDirtyProperties(false);
return media;
return content;
}
public void RebuildXmlStructures(Func<IMedia, XElement> serializer, int groupSize = 200, IEnumerable<int> contentTypeIds = null)
@@ -245,9 +266,10 @@ namespace Umbraco.Core.Persistence.Repositories
query = query
.WhereIn<ContentDto>(x => x.ContentTypeId, contentTypeIdsA, SqlSyntax);
query = query
.Where<NodeDto>(x => x.NodeId > baseId)
.Where<NodeDto>(x => x.NodeId > baseId, SqlSyntax)
.OrderBy<NodeDto>(x => x.NodeId, SqlSyntax);
var xmlItems = ProcessQuery(SqlSyntax.SelectTop(query, groupSize))
var sql = SqlSyntax.SelectTop(query, groupSize);
var xmlItems = ProcessQuery(sql, new PagingSqlQuery(sql))
.Select(x => new ContentXmlDto { NodeId = x.Id, Xml = serializer(x).ToString() })
.ToList();
@@ -495,54 +517,29 @@ namespace Umbraco.Core.Persistence.Repositories
filterCallback = () => new Tuple<string, object[]>(sbWhere.ToString().Trim(), args.ToArray());
}
return GetPagedResultsByQuery<ContentVersionDto, Models.Media>(query, pageIndex, pageSize, out totalRecords,
return GetPagedResultsByQuery<ContentVersionDto>(query, pageIndex, pageSize, out totalRecords,
new Tuple<string, string>("cmsContentVersion", "contentId"),
sql => ProcessQuery(sql), orderBy, orderDirection, orderBySystemField,
(sqlFull, pagingSqlQuery) => ProcessQuery(sqlFull, pagingSqlQuery), orderBy, orderDirection, orderBySystemField,
filterCallback);
}
/// <summary>
/// Gets paged media descendants as XML by path
/// </summary>
/// <param name="path">Path starts with</param>
/// <param name="pageIndex">Page number</param>
/// <param name="pageSize">Page size</param>
/// <param name="totalRecords">Total records the query would return without paging</param>
/// <returns>A paged enumerable of XML entries of media items</returns>
public IEnumerable<XElement> GetPagedXmlEntriesByPath(string path, long pageIndex, int pageSize, out long totalRecords)
{
Sql query;
if (path == "-1")
{
query = new Sql().Select("nodeId, xml").From("cmsContentXml").Where("nodeId IN (SELECT id FROM umbracoNode WHERE nodeObjectType = @0)", Guid.Parse(Constants.ObjectTypes.Media)).OrderBy("nodeId");
}
else
{
query = new Sql().Select("nodeId, xml").From("cmsContentXml").Where("nodeId IN (SELECT id FROM umbracoNode WHERE path LIKE @0)", path.EnsureEndsWith(",%")).OrderBy("nodeId");
}
var pagedResult = Database.Page<ContentXmlDto>(pageIndex+1, pageSize, query);
totalRecords = pagedResult.TotalItems;
return pagedResult.Items.Select(dto => XElement.Parse(dto.Xml));
}
/// <summary>
/// Private method to create a media object from a ContentDto
/// </summary>
/// <param name="d"></param>
/// <param name="dto"></param>
/// <param name="versionId"></param>
/// <param name="docSql"></param>
/// <returns></returns>
private IMedia CreateMediaFromDto(ContentVersionDto dto, Guid versionId, Sql docSql)
{
var contentType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId);
var factory = new MediaFactory(contentType, NodeObjectTypeId, dto.NodeId);
var media = factory.BuildEntity(dto);
var media = MediaFactory.BuildEntity(dto, contentType);
var docDef = new DocumentDefinition(dto.NodeId, versionId, media.UpdateDate, media.CreateDate, contentType);
var properties = GetPropertyCollection(docSql, new[] { docDef });
var properties = GetPropertyCollection(new PagingSqlQuery(docSql), new[] { docDef });
media.Properties = properties[dto.NodeId];
@@ -68,7 +68,7 @@ namespace Umbraco.Core.Persistence.Repositories
sql.Where("umbracoNode.id in (@ids)", new { ids = ids });
}
return ProcessQuery(sql);
return ProcessQuery(sql, new PagingSqlQuery(sql));
}
@@ -90,7 +90,7 @@ namespace Umbraco.Core.Persistence.Repositories
baseQuery.Append(new Sql("WHERE umbracoNode.id IN (" + sql.SQL + ")", sql.Arguments))
.OrderBy<NodeDto>(x => x.SortOrder);
return ProcessQuery(baseQuery);
return ProcessQuery(baseQuery, new PagingSqlQuery(baseQuery));
}
else
{
@@ -98,7 +98,7 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = translator.Translate()
.OrderBy<NodeDto>(x => x.SortOrder);
return ProcessQuery(sql);
return ProcessQuery(sql, new PagingSqlQuery(sql));
}
}
@@ -107,24 +107,29 @@ namespace Umbraco.Core.Persistence.Repositories
#region Overrides of PetaPocoRepositoryBase<int,IMembershipUser>
protected override Sql GetBaseQuery(bool isCount)
protected override Sql GetBaseQuery(BaseQueryType queryType)
{
var sql = new Sql();
sql.Select(isCount ? "COUNT(*)" : "*")
.From<MemberDto>()
.InnerJoin<ContentVersionDto>()
.On<ContentVersionDto, MemberDto>(left => left.NodeId, right => right.NodeId)
.InnerJoin<ContentDto>()
.On<ContentVersionDto, ContentDto>(left => left.NodeId, right => right.NodeId)
sql.Select(queryType == BaseQueryType.Count ? "COUNT(*)" : (queryType == BaseQueryType.Ids ? "cmsMember.nodeId" : "*"))
.From<MemberDto>(SqlSyntax)
.InnerJoin<ContentVersionDto>(SqlSyntax)
.On<ContentVersionDto, MemberDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
.InnerJoin<ContentDto>(SqlSyntax)
.On<ContentVersionDto, ContentDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
//We're joining the type so we can do a query against the member type - not sure if this adds much overhead or not?
// the execution plan says it doesn't so we'll go with that and in that case, it might be worth joining the content
// types by default on the document and media repo's so we can query by content type there too.
.InnerJoin<ContentTypeDto>().On<ContentTypeDto, ContentDto>(left => left.NodeId, right => right.ContentTypeId)
.InnerJoin<NodeDto>()
.On<ContentDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
.InnerJoin<ContentTypeDto>(SqlSyntax)
.On<ContentTypeDto, ContentDto>(SqlSyntax, left => left.NodeId, right => right.ContentTypeId)
.InnerJoin<NodeDto>(SqlSyntax)
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId, SqlSyntax);
return sql;
}
protected override Sql GetBaseQuery(bool isCount)
{
return GetBaseQuery(isCount ? BaseQueryType.Count : BaseQueryType.FullSingle);
}
protected override string GetBaseWhereClause()
@@ -380,8 +385,8 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = GetBaseQuery(false)
.Where(GetBaseWhereClause(), new { Id = id })
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
return ProcessQuery(sql, true);
}
return ProcessQuery(sql, new PagingSqlQuery(sql), true);
}
public void RebuildXmlStructures(Func<IMember, XElement> serializer, int groupSize = 200, IEnumerable<int> contentTypeIds = null)
{
@@ -403,7 +408,8 @@ namespace Umbraco.Core.Persistence.Repositories
query = query
.Where<NodeDto>(x => x.NodeId > baseId)
.OrderBy<NodeDto>(x => x.NodeId, SqlSyntax);
var xmlItems = ProcessQuery(SqlSyntax.SelectTop(query, groupSize))
var sql = SqlSyntax.SelectTop(query, groupSize);
var xmlItems = ProcessQuery(sql, new PagingSqlQuery(sql))
.Select(x => new ContentXmlDto { NodeId = x.Id, Xml = serializer(x).ToString() })
.ToList();
@@ -444,7 +450,7 @@ namespace Umbraco.Core.Persistence.Repositories
var factory = new MemberFactory(memberType, NodeObjectTypeId, dto.NodeId);
var media = factory.BuildEntity(dto);
var properties = GetPropertyCollection(sql, new[] { new DocumentDefinition(dto.NodeId, dto.ContentVersionDto.VersionId, media.UpdateDate, media.CreateDate, memberType) });
var properties = GetPropertyCollection(new PagingSqlQuery(sql), new[] { new DocumentDefinition(dto.NodeId, dto.ContentVersionDto.VersionId, media.UpdateDate, media.CreateDate, memberType) });
media.Properties = properties[dto.NodeId];
@@ -535,7 +541,7 @@ namespace Umbraco.Core.Persistence.Repositories
.OrderByDescending<ContentVersionDto>(x => x.VersionDate)
.OrderBy<NodeDto>(x => x.SortOrder);
return ProcessQuery(sql);
return ProcessQuery(sql, new PagingSqlQuery(sql));
}
@@ -617,9 +623,9 @@ namespace Umbraco.Core.Persistence.Repositories
filterCallback = () => new Tuple<string, object[]>(sbWhere.ToString().Trim(), args.ToArray());
}
return GetPagedResultsByQuery<MemberDto, Member>(query, pageIndex, pageSize, out totalRecords,
return GetPagedResultsByQuery<MemberDto>(query, pageIndex, pageSize, out totalRecords,
new Tuple<string, string>("cmsMember", "nodeId"),
sql => ProcessQuery(sql), orderBy, orderDirection, orderBySystemField,
(sqlFull, sqlIds) => ProcessQuery(sqlFull, sqlIds), orderBy, orderDirection, orderBySystemField,
filterCallback);
}
@@ -659,10 +665,21 @@ namespace Umbraco.Core.Persistence.Repositories
return base.GetEntityPropertyNameForOrderBy(orderBy);
}
private IEnumerable<IMember> ProcessQuery(Sql sql, bool withCache = false)
/// <summary>
/// This is the underlying method that processes most queries for this repository
/// </summary>
/// <param name="sqlFull">
/// The full SQL to select all member data
/// </param>
/// <param name="pagingSqlQuery">
/// The Id SQL to just return all member ids - used to process the properties for the member item
/// </param>
/// <param name="withCache"></param>
/// <returns></returns>
private IEnumerable<IMember> ProcessQuery(Sql sqlFull, PagingSqlQuery pagingSqlQuery, bool withCache = false)
{
// fetch returns a list so it's ok to iterate it in this method
var dtos = Database.Fetch<MemberDto, ContentVersionDto, ContentDto, NodeDto>(sql);
var dtos = Database.Fetch<MemberDto, ContentVersionDto, ContentDto, NodeDto>(sqlFull);
var content = new IMember[dtos.Count];
var defs = new List<DocumentDefinition>();
@@ -699,7 +716,7 @@ namespace Umbraco.Core.Persistence.Repositories
}
// load all properties for all documents from database in 1 query
var propertyData = GetPropertyCollection(sql, defs);
var propertyData = GetPropertyCollection(pagingSqlQuery, defs);
// assign
var dtoIndex = 0;
@@ -63,6 +63,7 @@ namespace Umbraco.Core.Persistence.Repositories
FormatDeleteStatement("cmsContentVersion", "ContentId"),
FormatDeleteStatement("cmsContentXml", "nodeId"),
FormatDeleteStatement("cmsContent", "nodeId"),
//TODO: Why is this being done? We just delete this exact data in the next line
"UPDATE umbracoNode SET parentID = '" + RecycleBinId + "' WHERE trashed = '1' AND nodeObjectType = @NodeObjectType",
"DELETE FROM umbracoNode WHERE trashed = '1' AND nodeObjectType = @NodeObjectType"
};
@@ -91,14 +92,18 @@ namespace Umbraco.Core.Persistence.Repositories
}
}
/// <summary>
/// A delete statement that will delete anything in the table specified where its PK (keyName) is found in the
/// list of umbracoNode.id that have trashed flag set
/// </summary>
/// <param name="tableName"></param>
/// <param name="keyName"></param>
/// <returns></returns>
private string FormatDeleteStatement(string tableName, string keyName)
{
//This query works with sql ce and sql server:
//DELETE FROM umbracoUser2NodeNotify WHERE umbracoUser2NodeNotify.nodeId IN
//(SELECT nodeId FROM umbracoUser2NodeNotify as TB1 INNER JOIN umbracoNode as TB2 ON TB1.nodeId = TB2.id WHERE TB2.trashed = '1' AND TB2.nodeObjectType = 'C66BA18E-EAF3-4CFF-8A22-41B16D66A972')
return
string.Format(
"DELETE FROM {0} WHERE {0}.{1} IN (SELECT TB1.{1} FROM {0} as TB1 INNER JOIN umbracoNode as TB2 ON TB1.{1} = TB2.id WHERE TB2.trashed = '1' AND TB2.nodeObjectType = @NodeObjectType)",
"DELETE FROM {0} WHERE {0}.{1} IN (SELECT id FROM umbracoNode WHERE trashed = '1' AND nodeObjectType = @NodeObjectType)",
tableName, keyName);
}
@@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
@@ -25,8 +27,6 @@ using Umbraco.Core.IO;
namespace Umbraco.Core.Persistence.Repositories
{
using SqlSyntax;
internal abstract class VersionableRepositoryBase<TId, TEntity> : PetaPocoRepositoryBase<TId, TEntity>
where TEntity : class, IAggregateRoot
{
@@ -137,6 +137,40 @@ namespace Umbraco.Core.Persistence.Repositories
#endregion
/// <summary>
/// Gets paged document descendants as XML by path
/// </summary>
/// <param name="path">Path starts with</param>
/// <param name="pageIndex">Page number</param>
/// <param name="pageSize">Page size</param>
/// <param name="orderBy"></param>
/// <param name="totalRecords">Total records the query would return without paging</param>
/// <returns>A paged enumerable of XML entries of content items</returns>
public virtual IEnumerable<XElement> GetPagedXmlEntriesByPath(string path, long pageIndex, int pageSize, string[] orderBy, out long totalRecords)
{
var query = new Sql().Select(string.Format("umbracoNode.id, cmsContentXml.{0}", SqlSyntax.GetQuotedColumnName("xml")))
.From("umbracoNode")
.InnerJoin("cmsContentXml").On("cmsContentXml.nodeId = umbracoNode.id");
if (path == "-1")
{
query.Where("umbracoNode.nodeObjectType = @type", new { type = NodeObjectTypeId });
}
else
{
query.Where(string.Format("umbracoNode.{0} LIKE (@0)", SqlSyntax.GetQuotedColumnName("path")), path.EnsureEndsWith(",%"));
}
//each order by param needs to be in a bracket! see: https://github.com/toptensoftware/PetaPoco/issues/177
query.OrderBy(orderBy == null
? "(umbracoNode.id)"
: string.Join(",", orderBy.Select(x => string.Format("({0})", SqlSyntax.GetQuotedColumnName(x)))));
var pagedResult = Database.Page<ContentXmlDto>(pageIndex + 1, pageSize, query);
totalRecords = pagedResult.TotalItems;
return pagedResult.Items.Select(dto => XElement.Parse(dto.Xml));
}
public int CountDescendants(int parentId, string contentTypeAlias = null)
{
var pathMatch = parentId == -1
@@ -348,12 +382,8 @@ namespace Umbraco.Core.Persistence.Repositories
ON CustomPropData.CustomPropValContentId = umbracoNode.id
", sortedInt, sortedDecimal, sortedDate, sortedString, nodeIdSelect.Item2, nodeIdSelect.Item1, versionQuery, sortedSql.Arguments.Length, newestQuery);
//insert this just above the first LEFT OUTER JOIN (for cmsDocument) or the last WHERE (everything else)
string newSql;
if (nodeIdSelect.Item1 == "cmsDocument")
newSql = sortedSql.SQL.Insert(sortedSql.SQL.IndexOf("LEFT OUTER JOIN"), outerJoinTempTable);
else
newSql = sortedSql.SQL.Insert(sortedSql.SQL.LastIndexOf("WHERE"), outerJoinTempTable);
//insert this just above the last WHERE
string newSql = sortedSql.SQL.Insert(sortedSql.SQL.LastIndexOf("WHERE"), outerJoinTempTable);
var newArgs = sortedSql.Arguments.ToList();
newArgs.Add(orderBy);
@@ -370,11 +400,14 @@ namespace Umbraco.Core.Persistence.Repositories
}
}
//no matter what we always MUST order the result also by umbracoNode.id to ensure that all records being ordered by are unique.
// if we do not do this then we end up with issues where we are ordering by a field that has duplicate values (i.e. the 'text' column
// is empty for many nodes)
// see: http://issues.umbraco.org/issue/U4-8831
sortedSql.OrderBy("umbracoNode.id");
if (orderBySystemField && orderBy != "umbracoNode.id")
{
//no matter what we always MUST order the result also by umbracoNode.id to ensure that all records being ordered by are unique.
// if we do not do this then we end up with issues where we are ordering by a field that has duplicate values (i.e. the 'text' column
// is empty for many nodes)
// see: http://issues.umbraco.org/issue/U4-8831
sortedSql.OrderBy("umbracoNode.id");
}
return sortedSql;
@@ -384,7 +417,6 @@ namespace Umbraco.Core.Persistence.Repositories
/// A helper method for inheritors to get the paged results by query in a way that minimizes queries
/// </summary>
/// <typeparam name="TDto">The type of the d.</typeparam>
/// <typeparam name="TContentBase">The 'true' entity type (i.e. Content, Member, etc...)</typeparam>
/// <param name="query">The query.</param>
/// <param name="pageIndex">Index of the page.</param>
/// <param name="pageSize">Size of the page.</param>
@@ -397,34 +429,30 @@ namespace Umbraco.Core.Persistence.Repositories
/// <param name="orderBySystemField">Flag to indicate when ordering by system field</param>
/// <returns></returns>
/// <exception cref="System.ArgumentNullException">orderBy</exception>
protected IEnumerable<TEntity> GetPagedResultsByQuery<TDto, TContentBase>(IQuery<TEntity> query, long pageIndex, int pageSize, out long totalRecords,
protected IEnumerable<TEntity> GetPagedResultsByQuery<TDto>(IQuery<TEntity> query, long pageIndex, int pageSize, out long totalRecords,
Tuple<string, string> nodeIdSelect,
Func<Sql, IEnumerable<TEntity>> processQuery,
Func<Sql, PagingSqlQuery<TDto>, IEnumerable<TEntity>> processQuery,
string orderBy,
Direction orderDirection,
bool orderBySystemField,
Func<Tuple<string, object[]>> defaultFilter = null)
where TContentBase : class, IAggregateRoot, TEntity
{
if (orderBy == null) throw new ArgumentNullException("orderBy");
// Get base query
var sqlBase = GetBaseQuery(false);
// Get base query for returning IDs
var sqlBaseIds = GetBaseQuery(BaseQueryType.Ids);
// Get base query for returning all data
var sqlBaseFull = GetBaseQuery(BaseQueryType.FullMultiple);
if (query == null) query = new Query<TEntity>();
var translator = new SqlTranslator<TEntity>(sqlBase, query);
var sqlQuery = translator.Translate();
// Note we can't do multi-page for several DTOs like we can multi-fetch and are doing in PerformGetByQuery,
// but actually given we are doing a Get on each one (again as in PerformGetByQuery), we only need the node Id.
// So we'll modify the SQL.
var sqlNodeIds = new Sql(
sqlQuery.SQL.Replace("SELECT *", string.Format("SELECT {0}.{1}", nodeIdSelect.Item1, nodeIdSelect.Item2)),
sqlQuery.Arguments);
var translatorIds = new SqlTranslator<TEntity>(sqlBaseIds, query);
var sqlQueryIds = translatorIds.Translate();
var translatorFull = new SqlTranslator<TEntity>(sqlBaseFull, query);
var sqlQueryFull = translatorFull.Translate();
//get sorted and filtered sql
var sqlNodeIdsWithSort = GetSortedSqlForPagedResults(
GetFilteredSqlForPagedResults(sqlNodeIds, defaultFilter),
GetFilteredSqlForPagedResults(sqlQueryIds, defaultFilter),
orderDirection, orderBy, orderBySystemField, nodeIdSelect);
// Get page of results and total count
@@ -433,38 +461,32 @@ namespace Umbraco.Core.Persistence.Repositories
totalRecords = Convert.ToInt32(pagedResult.TotalItems);
//NOTE: We need to check the actual items returned, not the 'totalRecords', that is because if you request a page number
// that doesn't actually have any data on it, the totalRecords will still indicate there are records but there are none in
// that doesn't actually have any data on it, the totalRecords will still indicate there are records but there are none in
// the pageResult, then the GetAll will actually return ALL records in the db.
if (pagedResult.Items.Any())
{
//Crete the inner paged query that was used above to get the paged result, we'll use that as the inner sub query
//Create the inner paged query that was used above to get the paged result, we'll use that as the inner sub query
var args = sqlNodeIdsWithSort.Arguments;
string sqlStringCount, sqlStringPage;
Database.BuildPageQueries<TDto>(pageIndex * pageSize, pageSize, sqlNodeIdsWithSort.SQL, ref args, out sqlStringCount, out sqlStringPage);
//if this is for sql server, the sqlPage will start with a SELECT * but we don't want that, we only want to return the nodeId
sqlStringPage = sqlStringPage
.Replace("SELECT *",
//This ensures we only take the field name of the node id select and not the table name - since the resulting select
// will ony work with the field name.
"SELECT " + nodeIdSelect.Item2);
//We need to make this an inner join on the paged query
var splitQuery = sqlQuery.SQL.Split(new[] { "WHERE " }, StringSplitOptions.None);
var withInnerJoinSql = new Sql(splitQuery[0])
//We need to make this FULL query an inner join on the paged ID query
var splitQuery = sqlQueryFull.SQL.Split(new[] { "WHERE " }, StringSplitOptions.None);
var fullQueryWithPagedInnerJoin = new Sql(splitQuery[0])
.Append("INNER JOIN (")
//join the paged query with the paged query arguments
.Append(sqlStringPage, args)
.Append(") temp ")
.Append(string.Format("ON {0}.{1} = temp.{1}", nodeIdSelect.Item1, nodeIdSelect.Item2))
//add the original where clause back with the original arguments
.Where(splitQuery[1], sqlQuery.Arguments);
.Where(splitQuery[1], sqlQueryIds.Arguments);
//get sorted and filtered sql
var fullQuery = GetSortedSqlForPagedResults(
GetFilteredSqlForPagedResults(withInnerJoinSql, defaultFilter),
GetFilteredSqlForPagedResults(fullQueryWithPagedInnerJoin, defaultFilter),
orderDirection, orderBy, orderBySystemField, nodeIdSelect);
return processQuery(fullQuery);
return processQuery(fullQuery, new PagingSqlQuery<TDto>(Database, sqlNodeIdsWithSort, pageIndex, pageSize));
}
else
{
@@ -474,38 +496,56 @@ namespace Umbraco.Core.Persistence.Repositories
return result;
}
/// <summary>
/// Gets the property collection for a non-paged query
/// </summary>
/// <param name="sql"></param>
/// <param name="documentDefs"></param>
/// <returns></returns>
protected IDictionary<int, PropertyCollection> GetPropertyCollection(
Sql docSql,
IEnumerable<DocumentDefinition> documentDefs)
Sql sql,
IReadOnlyCollection<DocumentDefinition> documentDefs)
{
if (documentDefs.Any() == false) return new Dictionary<int, PropertyCollection>();
return GetPropertyCollection(new PagingSqlQuery(sql), documentDefs);
}
//we need to parse the original SQL statement and reduce the columns to just cmsContent.nodeId, cmsContentVersion.VersionId so that we can use
/// <summary>
/// Gets the property collection for a query
/// </summary>
/// <param name="pagingSqlQuery"></param>
/// <param name="documentDefs"></param>
/// <returns></returns>
protected IDictionary<int, PropertyCollection> GetPropertyCollection(
PagingSqlQuery pagingSqlQuery,
IReadOnlyCollection<DocumentDefinition> documentDefs)
{
if (documentDefs.Count == 0) return new Dictionary<int, PropertyCollection>();
//initialize to the query passed in
var docSql = pagingSqlQuery.PrePagedSql;
//we need to parse the original SQL statement and reduce the columns to just cmsContent.nodeId, cmsContentVersion.VersionId so that we can use
// the statement to go get the property data for all of the items by using an inner join
var parsedOriginalSql = "SELECT {0} " + docSql.SQL.Substring(docSql.SQL.IndexOf("FROM", StringComparison.Ordinal));
//now remove everything from an Orderby clause and beyond
if (parsedOriginalSql.InvariantContains("ORDER BY "))
if (pagingSqlQuery.HasPaging)
{
//if this is a paged query, build the paged query with the custom column substitution, then re-assign
docSql = pagingSqlQuery.BuildPagedQuery("{0}");
parsedOriginalSql = docSql.SQL;
}
else if (parsedOriginalSql.InvariantContains("ORDER BY "))
{
//now remove everything from an Orderby clause and beyond if this is unpaged data
parsedOriginalSql = parsedOriginalSql.Substring(0, parsedOriginalSql.LastIndexOf("ORDER BY ", StringComparison.Ordinal));
}
var propSql = new Sql(@"SELECT cmsPropertyData.*
FROM cmsPropertyData
INNER JOIN cmsPropertyType
ON cmsPropertyData.propertytypeid = cmsPropertyType.id
INNER JOIN
(" + string.Format(parsedOriginalSql, "cmsContent.nodeId, cmsContentVersion.VersionId") + @") as docData
ON cmsPropertyData.versionId = docData.VersionId AND cmsPropertyData.contentNodeId = docData.nodeId
LEFT OUTER JOIN cmsDataTypePreValues
ON cmsPropertyType.dataTypeId = cmsDataTypePreValues.datatypeNodeId", docSql.Arguments);
var allPropertyData = Database.Fetch<PropertyDataDto>(propSql);
//This is a lazy access call to get all prevalue data for the data types that make up all of these properties which we use
// below if any property requires tag support
var allPreValues = new Lazy<IEnumerable<DataTypePreValueDto>>(() =>
{
var preValsSql = new Sql(@"SELECT a.id, a.value, a.sortorder, a.alias, a.datatypeNodeId
//This retrieves all pre-values for all data types that are referenced for all property types
// that exist in the data set.
//Benchmarks show that eagerly loading these so that we can lazily read the property data
// below (with the use of Query intead of Fetch) go about 30% faster, so we'll eagerly load
// this now since we cannot execute another reader inside of reading the property data.
var preValsSql = new Sql(@"SELECT a.id, a.value, a.sortorder, a.alias, a.datatypeNodeId
FROM cmsDataTypePreValues a
WHERE EXISTS(
SELECT DISTINCT b.id as preValIdInner
@@ -513,29 +553,82 @@ WHERE EXISTS(
INNER JOIN cmsPropertyType
ON b.datatypeNodeId = cmsPropertyType.dataTypeId
INNER JOIN
(" + string.Format(parsedOriginalSql, "DISTINCT cmsContent.contentType") + @") as docData
(" + string.Format(parsedOriginalSql, "cmsContent.contentType") + @") as docData
ON cmsPropertyType.contentTypeId = docData.contentType
WHERE a.id = b.id)", docSql.Arguments);
return Database.Fetch<DataTypePreValueDto>(preValsSql);
});
var allPreValues = Database.Fetch<DataTypePreValueDto>(preValsSql);
//It's Important with the sort order here! We require this to be sorted by node id,
// this is required because this data set can be huge depending on the page size. Due
// to it's size we need to be smart about iterating over the property values to build
// the document. Before we used to use Linq to get the property data for a given content node
// and perform a Distinct() call. This kills performance because that would mean if we had 7000 nodes
// and on each iteration we will perform a lookup on potentially 100,000 property rows against the node
// id which turns out to be a crazy amount of iterations. Instead we know it's sorted by this value we'll
// keep an index stored of the rows being read so we never have to re-iterate the entire data set
// on each document iteration.
var propSql = new Sql(@"SELECT cmsPropertyData.*
FROM cmsPropertyData
INNER JOIN cmsPropertyType
ON cmsPropertyData.propertytypeid = cmsPropertyType.id
INNER JOIN
(" + string.Format(parsedOriginalSql, "cmsContent.nodeId, cmsContentVersion.VersionId") + @") as docData
ON cmsPropertyData.versionId = docData.VersionId AND cmsPropertyData.contentNodeId = docData.nodeId
ORDER BY contentNodeId, propertytypeid
", docSql.Arguments);
//This does NOT fetch all data into memory in a list, this will read
// over the records as a data reader, this is much better for performance and memory,
// but it means that during the reading of this data set, nothing else can be read
// from SQL server otherwise we'll get an exception.
var allPropertyData = Database.Query<PropertyDataDto>(propSql);
var result = new Dictionary<int, PropertyCollection>();
var propertiesWithTagSupport = new Dictionary<string, SupportTagsAttribute>();
//used to track the resolved composition property types per content type so we don't have to re-resolve (ToArray) the list every time
var resolvedCompositionProperties = new Dictionary<int, PropertyType[]>();
//iterate each definition grouped by it's content type - this will mean less property type iterations while building
// up the property collections
foreach (var compositionGroup in documentDefs.GroupBy(x => x.Composition))
//keep track of the current property data item being enumerated
var propertyDataSetEnumerator = allPropertyData.GetEnumerator();
var hasCurrent = false; // initially there is no enumerator.Current
try
{
var compositionProperties = compositionGroup.Key.CompositionPropertyTypes.ToArray();
foreach (var def in compositionGroup)
//This must be sorted by node id because this is how we are sorting the query to lookup property types above,
// which allows us to more efficiently iterate over the large data set of property values
foreach (var def in documentDefs.OrderBy(x => x.Id))
{
var propertyDataDtos = allPropertyData.Where(x => x.NodeId == def.Id).Distinct();
// get the resolved properties from our local cache, or resolve them and put them in cache
PropertyType[] compositionProperties;
if (resolvedCompositionProperties.ContainsKey(def.Composition.Id))
{
compositionProperties = resolvedCompositionProperties[def.Composition.Id];
}
else
{
compositionProperties = def.Composition.CompositionPropertyTypes.ToArray();
resolvedCompositionProperties[def.Composition.Id] = compositionProperties;
}
var propertyFactory = new PropertyFactory(compositionProperties, def.Version, def.Id, def.CreateDate, def.VersionDate);
var properties = propertyFactory.BuildEntity(propertyDataDtos.ToArray()).ToArray();
// assemble the dtos for this def
// use the available enumerator.Current if any else move to next
var propertyDataDtos = new List<PropertyDataDto>();
while (hasCurrent || propertyDataSetEnumerator.MoveNext())
{
if (propertyDataSetEnumerator.Current.NodeId == def.Id)
{
hasCurrent = false; // enumerator.Current is not available
propertyDataDtos.Add(propertyDataSetEnumerator.Current);
}
else
{
hasCurrent = true; // enumerator.Current is available for another def
break; // no more propertyDataDto for this def
}
}
var properties = PropertyFactory.BuildEntity(propertyDataDtos, compositionProperties, def.CreateDate, def.VersionDate).ToArray();
foreach (var property in properties)
{
@@ -552,7 +645,7 @@ WHERE EXISTS(
propertiesWithTagSupport[property.PropertyType.PropertyEditorAlias] = tagSupport;
//this property has tags, so we need to extract them and for that we need the prevals which we've already looked up
var preValData = allPreValues.Value.Where(x => x.DataTypeNodeId == property.PropertyType.DataTypeDefinitionId)
var preValData = allPreValues.Where(x => x.DataTypeNodeId == property.PropertyType.DataTypeDefinitionId)
.Distinct()
.ToArray();
@@ -575,31 +668,15 @@ WHERE EXISTS(
result[def.Id] = new PropertyCollection(properties);
}
}
return result;
}
public class DocumentDefinition
{
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Object"/> class.
/// </summary>
public DocumentDefinition(int id, Guid version, DateTime versionDate, DateTime createDate, IContentTypeComposition composition)
finally
{
Id = id;
Version = version;
VersionDate = versionDate;
CreateDate = createDate;
Composition = composition;
propertyDataSetEnumerator.Dispose();
}
public int Id { get; set; }
public Guid Version { get; set; }
public DateTime VersionDate { get; set; }
public DateTime CreateDate { get; set; }
public IContentTypeComposition Composition { get; set; }
}
return result;
}
protected virtual string GetDatabaseFieldNameForOrderBy(string orderBy)
{
// Translate the passed order by field (which were originally defined for in-memory object sorting
@@ -688,5 +765,99 @@ WHERE EXISTS(
return allsuccess;
}
/// <summary>
/// For Paging, repositories must support returning different query for the query type specified
/// </summary>
/// <param name="queryType"></param>
/// <returns></returns>
protected abstract Sql GetBaseQuery(BaseQueryType queryType);
internal class DocumentDefinition
{
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Object"/> class.
/// </summary>
public DocumentDefinition(int id, Guid version, DateTime versionDate, DateTime createDate, IContentTypeComposition composition)
{
Id = id;
Version = version;
VersionDate = versionDate;
CreateDate = createDate;
Composition = composition;
}
public int Id { get; set; }
public Guid Version { get; set; }
public DateTime VersionDate { get; set; }
public DateTime CreateDate { get; set; }
public IContentTypeComposition Composition { get; set; }
}
/// <summary>
/// An object representing a query that may contain paging information
/// </summary>
internal class PagingSqlQuery
{
public Sql PrePagedSql { get; private set; }
public PagingSqlQuery(Sql prePagedSql)
{
PrePagedSql = prePagedSql;
}
public virtual bool HasPaging
{
get { return false; }
}
public virtual Sql BuildPagedQuery(string selectColumns)
{
throw new InvalidOperationException("This query has no paging information");
}
}
/// <summary>
/// An object representing a query that contains paging information
/// </summary>
/// <typeparam name="T"></typeparam>
internal class PagingSqlQuery<T> : PagingSqlQuery
{
private readonly Database _db;
private readonly long _pageIndex;
private readonly int _pageSize;
public PagingSqlQuery(Database db, Sql prePagedSql, long pageIndex, int pageSize) : base(prePagedSql)
{
_db = db;
_pageIndex = pageIndex;
_pageSize = pageSize;
}
public override bool HasPaging
{
get { return _pageSize > 0; }
}
/// <summary>
/// Creates a paged query based on the original query and subtitutes the selectColumns specified
/// </summary>
/// <param name="selectColumns"></param>
/// <returns></returns>
public override Sql BuildPagedQuery(string selectColumns)
{
if (HasPaging == false) throw new InvalidOperationException("This query has no paging information");
var resultSql = string.Format("SELECT {0} {1}", selectColumns, PrePagedSql.SQL.Substring(PrePagedSql.SQL.IndexOf("FROM", StringComparison.Ordinal)));
//this query is meant to be paged so we need to generate the paging syntax
//Create the inner paged query that was used above to get the paged result, we'll use that as the inner sub query
var args = PrePagedSql.Arguments;
string sqlStringCount, sqlStringPage;
_db.BuildPageQueries<T>(_pageIndex * _pageSize, _pageSize, resultSql, ref args, out sqlStringCount, out sqlStringPage);
return new Sql(sqlStringPage, args);
}
}
}
}
@@ -114,7 +114,7 @@ namespace Umbraco.Core.Security
AllowRefresh = true,
IssuedUtc = nowUtc,
ExpiresUtc = nowUtc.AddMinutes(GlobalSettings.TimeOutInMinutes)
}, userIdentity, rememberBrowserIdentity);
}, userIdentity, rememberBrowserIdentity);
}
else
{
@@ -127,6 +127,10 @@ namespace Umbraco.Core.Security
}, userIdentity);
}
//track the last login date
user.LastLoginDateUtc = DateTime.UtcNow;
await UserManager.UpdateAsync(user);
_logger.WriteCore(TraceEventType.Information, 0,
string.Format(
"Login attempt succeeded for username {0} from IP address {1}",
@@ -625,6 +625,12 @@ namespace Umbraco.Core.Security
var anythingChanged = false;
//don't assign anything if nothing has changed as this will trigger
//the track changes of the model
if ((user.LastLoginDate != default(DateTime) && identityUser.LastLoginDateUtc.HasValue == false)
|| identityUser.LastLoginDateUtc.HasValue && user.LastLoginDate.ToUniversalTime() != identityUser.LastLoginDateUtc.Value)
{
anythingChanged = true;
user.LastLoginDate = identityUser.LastLoginDateUtc.Value.ToLocalTime();
}
if (user.Name != identityUser.Name && identityUser.Name.IsNullOrWhiteSpace() == false)
{
anythingChanged = true;
+32 -2
View File
@@ -585,7 +585,7 @@ namespace Umbraco.Core.Services
[Obsolete("Use the overload with 'long' parameter types instead")]
[EditorBrowsable(EditorBrowsableState.Never)]
public IEnumerable<IContent> GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalChildren, string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "")
public IEnumerable<IContent> GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalChildren, string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "")
{
long total;
var result = GetPagedDescendants(id, Convert.ToInt64(pageIndex), pageSize, out total, orderBy, orderDirection, true, filter);
@@ -604,7 +604,7 @@ namespace Umbraco.Core.Services
/// <param name="orderDirection">Direction to order by</param>
/// <param name="filter">Search text filter</param>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable<IContent> GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "")
public IEnumerable<IContent> GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "")
{
return GetPagedDescendants(id, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, true, filter);
}
@@ -1764,6 +1764,31 @@ namespace Umbraco.Core.Services
return true;
}
/// <summary>
/// Gets paged content descendants as XML by path
/// </summary>
/// <param name="path">Path starts with</param>
/// <param name="pageIndex">Page number</param>
/// <param name="pageSize">Page size</param>
/// <param name="totalRecords">Total records the query would return without paging</param>
/// <returns>A paged enumerable of XML entries of content items</returns>
public IEnumerable<XElement> GetPagedXmlEntries(string path, long pageIndex, int pageSize, out long totalRecords)
{
Mandate.ParameterCondition(pageIndex >= 0, "pageIndex");
Mandate.ParameterCondition(pageSize > 0, "pageSize");
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateContentRepository(uow))
{
var contents = repository.GetPagedXmlEntriesByPath(path, pageIndex, pageSize,
//This order by is VERY important! This allows us to figure out what is implicitly not published, see ContentRepository.BuildXmlCache and
// UmbracoContentIndexer.PerformIndexAll which uses the logic based on this sort order
new[] {"level", "parentID", "sortOrder"},
out totalRecords);
return contents;
}
}
/// <summary>
/// This builds the Xml document used for the XML cache
/// </summary>
@@ -2192,6 +2217,11 @@ namespace Umbraco.Core.Services
}
}
if (string.IsNullOrWhiteSpace(content.Name))
{
throw new ArgumentException("Cannot save content with empty name.");
}
using (new WriteLock(Locker))
{
var uow = UowProvider.GetUnitOfWork();
@@ -719,8 +719,13 @@ namespace Umbraco.Core.Services
/// <param name="userId">Optional id of the user saving the ContentType</param>
public void Save(IContentType contentType, int userId = 0)
{
if (SavingContentType.IsRaisedEventCancelled(new SaveEventArgs<IContentType>(contentType), this))
return;
if (SavingContentType.IsRaisedEventCancelled(new SaveEventArgs<IContentType>(contentType), this))
return;
if (string.IsNullOrWhiteSpace(contentType.Name))
{
throw new ArgumentException("Cannot save content type with empty name.");
}
using (new WriteLock(Locker))
{
+7 -2
View File
@@ -361,8 +361,13 @@ namespace Umbraco.Core.Services
/// <param name="userId">Id of the user issueing the save</param>
public void Save(IDataTypeDefinition dataTypeDefinition, int userId = 0)
{
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinition), this))
return;
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinition), this))
return;
if (string.IsNullOrWhiteSpace(dataTypeDefinition.Name))
{
throw new ArgumentException("Cannot save datatype with empty name.");
}
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateDataTypeDefinitionRepository(uow))
+16 -2
View File
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Xml;
using System.Xml.Linq;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
@@ -95,6 +96,19 @@ namespace Umbraco.Core.Services
/// </summary>
public interface IContentService : IService
{
/// <summary>
/// Gets all XML entries found in the cmsContentXml table based on the given path
/// </summary>
/// <param name="path">Path starts with</param>
/// <param name="pageIndex">Page number</param>
/// <param name="pageSize">Page size</param>
/// <param name="totalRecords">Total records the query would return without paging</param>
/// <returns>A paged enumerable of XML entries of content items</returns>
/// <remarks>
/// If -1 is passed, then this will return all content xml entries, otherwise will return all descendents from the path
/// </remarks>
IEnumerable<XElement> GetPagedXmlEntries(string path, long pageIndex, int pageSize, out long totalRecords);
/// <summary>
/// This builds the Xml document used for the XML cache
/// </summary>
@@ -245,7 +259,7 @@ namespace Umbraco.Core.Services
[Obsolete("Use the overload with 'long' parameter types instead")]
[EditorBrowsable(EditorBrowsableState.Never)]
IEnumerable<IContent> GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalRecords,
string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "");
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "");
/// <summary>
/// Gets a collection of <see cref="IContent"/> objects by Parent Id
@@ -259,7 +273,7 @@ namespace Umbraco.Core.Services
/// <param name="filter">Search text filter</param>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
IEnumerable<IContent> GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords,
string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "");
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "");
/// <summary>
/// Gets a collection of <see cref="IContent"/> objects by Parent Id
+2 -2
View File
@@ -169,7 +169,7 @@ namespace Umbraco.Core.Services
[Obsolete("Use the overload with 'long' parameter types instead")]
[EditorBrowsable(EditorBrowsableState.Never)]
IEnumerable<IMedia> GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalRecords,
string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "");
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "");
/// <summary>
/// Gets a collection of <see cref="IMedia"/> objects by Parent Id
@@ -183,7 +183,7 @@ namespace Umbraco.Core.Services
/// <param name="filter">Search text filter</param>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
IEnumerable<IMedia> GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords,
string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "");
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "");
/// <summary>
/// Gets a collection of <see cref="IMedia"/> objects by Parent Id
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Xml.Linq;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
@@ -13,6 +14,15 @@ namespace Umbraco.Core.Services
/// </summary>
public interface IMemberService : IMembershipMemberService
{
/// <summary>
/// Gets all XML entries found in the cmsContentXml table
/// </summary>
/// <param name="pageIndex">Page number</param>
/// <param name="pageSize">Page size</param>
/// <param name="totalRecords">Total records the query would return without paging</param>
/// <returns>A paged enumerable of XML entries of content items</returns>
IEnumerable<XElement> GetPagedXmlEntries(long pageIndex, int pageSize, out long totalRecords);
/// <summary>
/// Rebuilds all xml content in the cmsContentXml table for all documents
/// </summary>
+16 -11
View File
@@ -142,19 +142,24 @@ namespace Umbraco.Core.Services
/// <param name="userId">Optional Id of the user deleting the macro</param>
public void Save(IMacro macro, int userId = 0)
{
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IMacro>(macro), this))
return;
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateMacroRepository(uow))
{
repository.AddOrUpdate(macro);
uow.Commit();
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IMacro>(macro), this))
return;
Saved.RaiseEvent(new SaveEventArgs<IMacro>(macro, false), this);
}
if (string.IsNullOrWhiteSpace(macro.Name))
{
throw new ArgumentException("Cannot save macro with empty name.");
}
Audit(AuditType.Save, "Save Macro performed by user", userId, -1);
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateMacroRepository(uow))
{
repository.AddOrUpdate(macro);
uow.Commit();
Saved.RaiseEvent(new SaveEventArgs<IMacro>(macro, false), this);
}
Audit(AuditType.Save, "Save Macro performed by user", userId, -1);
}
///// <summary>
+7 -3
View File
@@ -451,7 +451,7 @@ namespace Umbraco.Core.Services
[Obsolete("Use the overload with 'long' parameter types instead")]
[EditorBrowsable(EditorBrowsableState.Never)]
public IEnumerable<IMedia> GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalChildren, string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "")
public IEnumerable<IMedia> GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalChildren, string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "")
{
long total;
var result = GetPagedDescendants(id, Convert.ToInt64(pageIndex), pageSize, out total, orderBy, orderDirection, true, filter);
@@ -470,7 +470,7 @@ namespace Umbraco.Core.Services
/// <param name="orderDirection">Direction to order by</param>
/// <param name="filter">Search text filter</param>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable<IMedia> GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "")
public IEnumerable<IMedia> GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "")
{
return GetPagedDescendants(id, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, true, filter);
}
@@ -837,7 +837,11 @@ namespace Umbraco.Core.Services
{
return OperationStatus.Cancelled(evtMsgs);
}
}
if (string.IsNullOrWhiteSpace(media.Name))
{
throw new ArgumentException("Cannot save media with empty name.");
}
var uow = UowProvider.GetUnitOfWork();
@@ -1224,7 +1228,7 @@ namespace Umbraco.Core.Services
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateMediaRepository(uow))
{
var contents = repository.GetPagedXmlEntriesByPath(path, pageIndex, pageSize, out totalRecords);
var contents = repository.GetPagedXmlEntriesByPath(path, pageIndex, pageSize, null, out totalRecords);
return contents;
}
}
@@ -612,6 +612,26 @@ namespace Umbraco.Core.Services
Audit(AuditType.Publish, "MemberService.RebuildXmlStructures completed, the xml has been regenerated in the database", 0, -1);
}
/// <summary>
/// Gets paged member descendants as XML by path
/// </summary>
/// <param name="pageIndex">Page number</param>
/// <param name="pageSize">Page size</param>
/// <param name="totalRecords">Total records the query would return without paging</param>
/// <returns>A paged enumerable of XML entries of member items</returns>
public IEnumerable<XElement> GetPagedXmlEntries(long pageIndex, int pageSize, out long totalRecords)
{
Mandate.ParameterCondition(pageIndex >= 0, "pageIndex");
Mandate.ParameterCondition(pageSize > 0, "pageSize");
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateMemberRepository(uow))
{
var contents = repository.GetPagedXmlEntriesByPath("-1", pageIndex, pageSize, null, out totalRecords);
return contents;
}
}
#endregion
#region IMembershipMemberService Implementation
@@ -978,6 +998,11 @@ namespace Umbraco.Core.Services
}
}
if (string.IsNullOrWhiteSpace(entity.Name))
{
throw new ArgumentException("Cannot save member with empty name.");
}
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateMemberRepository(uow))
{
@@ -78,6 +78,11 @@ namespace Umbraco.Core.Services
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IMemberType>(memberType), this))
return;
if (string.IsNullOrWhiteSpace(memberType.Name))
{
throw new ArgumentException("Cannot save MemberType with empty name.");
}
using (new WriteLock(Locker))
{
var uow = UowProvider.GetUnitOfWork();
+22
View File
@@ -125,6 +125,11 @@ namespace Umbraco.Core.Services
{
if (userType == null) throw new ArgumentNullException("userType");
if (string.IsNullOrWhiteSpace(username))
{
throw new ArgumentException("Cannot create user with empty username.");
}
//TODO: PUT lock here!!
var uow = UowProvider.GetUnitOfWork();
@@ -312,6 +317,15 @@ namespace Umbraco.Core.Services
return;
}
if (string.IsNullOrWhiteSpace(entity.Username))
{
throw new ArgumentException("Cannot save user with empty username.");
}
if (string.IsNullOrWhiteSpace(entity.Name))
{
throw new ArgumentException("Cannot save user with empty name.");
}
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateUserRepository(uow))
{
@@ -353,6 +367,14 @@ namespace Umbraco.Core.Services
{
foreach (var member in entities)
{
if (string.IsNullOrWhiteSpace(member.Username))
{
throw new ArgumentException("Cannot save user with empty username.");
}
if (string.IsNullOrWhiteSpace(member.Name))
{
throw new ArgumentException("Cannot save user with empty name.");
}
repository.AddOrUpdate(member);
}
//commit the whole lot in one go
+4 -2
View File
@@ -49,8 +49,9 @@
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
<HintPath>..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll</HintPath>
</Reference>
<Reference Include="ImageProcessor, Version=2.5.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ImageProcessor.2.5.1\lib\net45\ImageProcessor.dll</HintPath>
<Reference Include="ImageProcessor, Version=2.5.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ImageProcessor.2.5.2\lib\net45\ImageProcessor.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="log4net, Version=1.2.11.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\log4net-mediumtrust.2.0.0\lib\log4net.dll</HintPath>
@@ -466,6 +467,7 @@
<Compile Include="Persistence\RecordPersistenceType.cs" />
<Compile Include="Persistence\Relators\AccessRulesRelator.cs" />
<Compile Include="Persistence\Repositories\AuditRepository.cs" />
<Compile Include="Persistence\Repositories\BaseQueryType.cs" />
<Compile Include="Persistence\Repositories\EntityContainerRepository.cs" />
<Compile Include="Persistence\Repositories\DomainRepository.cs" />
<Compile Include="Persistence\Repositories\ExternalLoginRepository.cs" />
+1 -1
View File
@@ -2,7 +2,7 @@
<packages>
<package id="AutoMapper" version="3.3.1" targetFramework="net45" />
<package id="HtmlAgilityPack" version="1.4.9" targetFramework="net45" />
<package id="ImageProcessor" version="2.5.1" targetFramework="net45" />
<package id="ImageProcessor" version="2.5.2" targetFramework="net45" />
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net45" />
<package id="Microsoft.AspNet.Identity.Core" version="2.2.1" targetFramework="net45" />
<package id="Microsoft.AspNet.Identity.Owin" version="2.2.1" targetFramework="net45" />
+4 -1
View File
@@ -13,7 +13,10 @@ namespace Umbraco.Tests.Benchmarks
typeof(BulkInsertBenchmarks),
typeof(ModelToSqlExpressionHelperBenchmarks),
typeof(XmlBenchmarks),
typeof(LinqCastBenchmarks)
typeof(LinqCastBenchmarks),
//typeof(DeepCloneBenchmarks),
typeof(XmlPublishedContentInitBenchmarks),
});
switcher.Run(args);
}
@@ -91,6 +91,7 @@
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="XmlBenchmarks.cs" />
<Compile Include="XmlPublishedContentInitBenchmarks.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
@@ -105,6 +106,10 @@
<Project>{5d3b8245-ada6-453f-a008-50ed04bfe770}</Project>
<Name>Umbraco.Tests</Name>
</ProjectReference>
<ProjectReference Include="..\Umbraco.Web\Umbraco.Web.csproj">
<Project>{651e1350-91b6-44b7-bd60-7207006d7003}</Project>
<Name>Umbraco.Web</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Analyzer Include="..\packages\Microsoft.CodeAnalysis.Analyzers.1.1.0\analyzers\dotnet\cs\Microsoft.CodeAnalysis.Analyzers.dll" />
@@ -0,0 +1,315 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnostics.Windows;
using BenchmarkDotNet.Jobs;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Web.PublishedCache.XmlPublishedCache;
namespace Umbraco.Tests.Benchmarks
{
[Config(typeof(Config))]
public class XmlPublishedContentInitBenchmarks
{
private class Config : ManualConfig
{
public Config()
{
Add(new MemoryDiagnoser());
//The 'quick and dirty' settings, so it runs a little quicker
// see benchmarkdotnet FAQ
Add(Job.Default
.WithLaunchCount(1) // benchmark process will be launched only once
.WithIterationTime(100) // 100ms per iteration
.WithWarmupCount(3) // 3 warmup iteration
.WithTargetCount(3)); // 3 target iteration
}
}
public XmlPublishedContentInitBenchmarks()
{
_xml10 = Build(10);
_xml100 = Build(100);
_xml1000 = Build(1000);
_xml10000 = Build(10000);
}
private readonly string[] _intAttributes = { "id", "parentID", "nodeType", "level", "writerID", "creatorID", "template", "sortOrder", "isDoc", "isDraft" };
private readonly string[] _strAttributes = { "nodeName", "urlName", "writerName", "creatorName", "path" };
private readonly string[] _dateAttributes = { "createDate", "updateDate" };
private readonly string[] _guidAttributes = { "key", "version" };
private XmlDocument Build(int children)
{
var xml = new XmlDocument();
var root = Build(xml, "Home", 10);
for (int i = 0; i < children; i++)
{
var child = Build(xml, "child" + i, 10);
root.AppendChild(child);
}
xml.AppendChild(root);
return xml;
}
private XmlElement Build(XmlDocument xml, string name, int propertyCount)
{
var random = new Random();
var content = xml.CreateElement(name);
foreach (var p in _intAttributes)
{
var a = xml.CreateAttribute(p);
a.Value = random.Next(1, 9).ToInvariantString();
content.Attributes.Append(a);
}
foreach (var p in _strAttributes)
{
var a = xml.CreateAttribute(p);
a.Value = Guid.NewGuid().ToString();
content.Attributes.Append(a);
}
foreach (var p in _guidAttributes)
{
var a = xml.CreateAttribute(p);
a.Value = Guid.NewGuid().ToString();
content.Attributes.Append(a);
}
foreach (var p in _dateAttributes)
{
var a = xml.CreateAttribute(p);
a.Value = DateTime.Now.ToString("o");
content.Attributes.Append(a);
}
for (int i = 0; i < propertyCount; i++)
{
var prop = xml.CreateElement("prop" + i);
var cdata = xml.CreateCDataSection(string.Join("", Enumerable.Range(0, 10).Select(x => Guid.NewGuid().ToString())));
prop.AppendChild(cdata);
content.AppendChild(prop);
}
return content;
}
private readonly XmlDocument _xml10;
private readonly XmlDocument _xml100;
private readonly XmlDocument _xml1000;
private readonly XmlDocument _xml10000;
//out props
int id, nodeType, level, writerId, creatorId, template, sortOrder;
Guid key, version;
string name, urlName, writerName, creatorName, docTypeAlias, path;
bool isDraft;
DateTime createDate, updateDate;
PublishedContentType publishedContentType;
Dictionary<string, IPublishedProperty> properties;
[Benchmark(Baseline = true, OperationsPerInvoke = 10)]
public void Original_10_Children()
{
OriginalInitializeNode(_xml10.DocumentElement, false, false,
out id, out key, out template, out sortOrder, out name, out writerName, out urlName,
out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path,
out version, out createDate, out updateDate, out level, out isDraft, out publishedContentType,
out properties);
}
[Benchmark(OperationsPerInvoke = 10)]
public void Original_100_Children()
{
OriginalInitializeNode(_xml100.DocumentElement, false, false,
out id, out key, out template, out sortOrder, out name, out writerName, out urlName,
out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path,
out version, out createDate, out updateDate, out level, out isDraft, out publishedContentType,
out properties);
}
[Benchmark(OperationsPerInvoke = 10)]
public void Original_1000_Children()
{
OriginalInitializeNode(_xml1000.DocumentElement, false, false,
out id, out key, out template, out sortOrder, out name, out writerName, out urlName,
out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path,
out version, out createDate, out updateDate, out level, out isDraft, out publishedContentType,
out properties);
}
[Benchmark(OperationsPerInvoke = 10)]
public void Original_10000_Children()
{
OriginalInitializeNode(_xml10000.DocumentElement, false, false,
out id, out key, out template, out sortOrder, out name, out writerName, out urlName,
out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path,
out version, out createDate, out updateDate, out level, out isDraft, out publishedContentType,
out properties);
}
[Benchmark(OperationsPerInvoke = 10)]
public void Enhanced_10_Children()
{
XmlPublishedContent.InitializeNode(_xml10.DocumentElement, false, false,
out id, out key, out template, out sortOrder, out name, out writerName, out urlName,
out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path,
out version, out createDate, out updateDate, out level, out isDraft, out publishedContentType,
out properties, GetPublishedContentType);
}
[Benchmark(OperationsPerInvoke = 10)]
public void Enhanced_100_Children()
{
XmlPublishedContent.InitializeNode(_xml100.DocumentElement, false, false,
out id, out key, out template, out sortOrder, out name, out writerName, out urlName,
out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path,
out version, out createDate, out updateDate, out level, out isDraft, out publishedContentType,
out properties, GetPublishedContentType);
}
[Benchmark(OperationsPerInvoke = 10)]
public void Enhanced_1000_Children()
{
XmlPublishedContent.InitializeNode(_xml1000.DocumentElement, false, false,
out id, out key, out template, out sortOrder, out name, out writerName, out urlName,
out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path,
out version, out createDate, out updateDate, out level, out isDraft, out publishedContentType,
out properties, GetPublishedContentType);
}
[Benchmark(OperationsPerInvoke = 10)]
public void Enhanced_10000_Children()
{
XmlPublishedContent.InitializeNode(_xml10000.DocumentElement, false, false,
out id, out key, out template, out sortOrder, out name, out writerName, out urlName,
out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path,
out version, out createDate, out updateDate, out level, out isDraft, out publishedContentType,
out properties, GetPublishedContentType);
}
internal static void OriginalInitializeNode(XmlNode xmlNode, bool legacy, bool isPreviewing,
out int id, out Guid key, out int template, out int sortOrder, out string name, out string writerName, out string urlName,
out string creatorName, out int creatorId, out int writerId, out string docTypeAlias, out int docTypeId, out string path,
out Guid version, out DateTime createDate, out DateTime updateDate, out int level, out bool isDraft,
out PublishedContentType contentType, out Dictionary<string, IPublishedProperty> properties)
{
//initialize the out params with defaults:
writerName = null;
docTypeAlias = null;
id = template = sortOrder = template = creatorId = writerId = docTypeId = level = default(int);
key = version = default(Guid);
name = writerName = urlName = creatorName = docTypeAlias = path = null;
createDate = updateDate = default(DateTime);
isDraft = false;
contentType = null;
properties = null;
//return if this is null
if (xmlNode == null)
{
return;
}
if (xmlNode.Attributes != null)
{
id = int.Parse(xmlNode.Attributes.GetNamedItem("id").Value);
if (xmlNode.Attributes.GetNamedItem("key") != null) // because, migration
key = Guid.Parse(xmlNode.Attributes.GetNamedItem("key").Value);
if (xmlNode.Attributes.GetNamedItem("template") != null)
template = int.Parse(xmlNode.Attributes.GetNamedItem("template").Value);
if (xmlNode.Attributes.GetNamedItem("sortOrder") != null)
sortOrder = int.Parse(xmlNode.Attributes.GetNamedItem("sortOrder").Value);
if (xmlNode.Attributes.GetNamedItem("nodeName") != null)
name = xmlNode.Attributes.GetNamedItem("nodeName").Value;
if (xmlNode.Attributes.GetNamedItem("writerName") != null)
writerName = xmlNode.Attributes.GetNamedItem("writerName").Value;
if (xmlNode.Attributes.GetNamedItem("urlName") != null)
urlName = xmlNode.Attributes.GetNamedItem("urlName").Value;
// Creatorname is new in 2.1, so published xml might not have it!
try
{
creatorName = xmlNode.Attributes.GetNamedItem("creatorName").Value;
}
catch
{
creatorName = writerName;
}
//Added the actual userID, as a user cannot be looked up via full name only...
if (xmlNode.Attributes.GetNamedItem("creatorID") != null)
creatorId = int.Parse(xmlNode.Attributes.GetNamedItem("creatorID").Value);
if (xmlNode.Attributes.GetNamedItem("writerID") != null)
writerId = int.Parse(xmlNode.Attributes.GetNamedItem("writerID").Value);
if (legacy)
{
if (xmlNode.Attributes.GetNamedItem("nodeTypeAlias") != null)
docTypeAlias = xmlNode.Attributes.GetNamedItem("nodeTypeAlias").Value;
}
else
{
docTypeAlias = xmlNode.Name;
}
if (xmlNode.Attributes.GetNamedItem("nodeType") != null)
docTypeId = int.Parse(xmlNode.Attributes.GetNamedItem("nodeType").Value);
if (xmlNode.Attributes.GetNamedItem("path") != null)
path = xmlNode.Attributes.GetNamedItem("path").Value;
if (xmlNode.Attributes.GetNamedItem("version") != null)
version = new Guid(xmlNode.Attributes.GetNamedItem("version").Value);
if (xmlNode.Attributes.GetNamedItem("createDate") != null)
createDate = DateTime.Parse(xmlNode.Attributes.GetNamedItem("createDate").Value);
if (xmlNode.Attributes.GetNamedItem("updateDate") != null)
updateDate = DateTime.Parse(xmlNode.Attributes.GetNamedItem("updateDate").Value);
if (xmlNode.Attributes.GetNamedItem("level") != null)
level = int.Parse(xmlNode.Attributes.GetNamedItem("level").Value);
isDraft = (xmlNode.Attributes.GetNamedItem("isDraft") != null);
}
// load data
var dataXPath = legacy ? "data" : "* [not(@isDoc)]";
var nodes = xmlNode.SelectNodes(dataXPath);
contentType = GetPublishedContentType(PublishedItemType.Content, docTypeAlias);
var propertyNodes = new Dictionary<string, XmlNode>();
if (nodes != null)
foreach (XmlNode n in nodes)
{
var attrs = n.Attributes;
if (attrs == null) continue;
var alias = legacy
? attrs.GetNamedItem("alias").Value
: n.Name;
propertyNodes[alias.ToLowerInvariant()] = n;
}
properties = contentType.PropertyTypes.Select(p =>
{
XmlNode n;
return propertyNodes.TryGetValue(p.PropertyTypeAlias.ToLowerInvariant(), out n)
? new XmlPublishedProperty(p, isPreviewing, n)
: new XmlPublishedProperty(p, isPreviewing);
}).Cast<IPublishedProperty>().ToDictionary(
x => x.PropertyTypeAlias,
x => x,
StringComparer.OrdinalIgnoreCase);
}
private static PublishedContentType GetPublishedContentType(PublishedItemType type, string alias)
{
return new PublishedContentType(alias, new string[] {},
new List<PublishedPropertyType>(Enumerable.Range(0, 10).Select(x => new PublishedPropertyType("prop" + x, 0, "test", initConverters:false))));
}
}
}
+190
View File
@@ -0,0 +1,190 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml.Serialization;
using NUnit.Framework;
using Umbraco.Tests.TestHelpers;
namespace Umbraco.Tests.Dependencies
{
[TestFixture]
public class NuGet
{
[Test]
public void NuGet_Package_Versions_Are_The_Same_In_All_Package_Config_Files()
{
var packagesAndVersions = GetNuGetPackagesInSolution();
Assert.IsTrue(packagesAndVersions.Any());
var failTest = false;
foreach (var package in packagesAndVersions.OrderBy(x => x.ConfigFilePath))
{
var matchingPackages = packagesAndVersions.Where(x => string.Equals(x.PackageName, package.PackageName, StringComparison.InvariantCultureIgnoreCase));
foreach (var matchingPackage in matchingPackages)
{
if (package.PackageVersion == matchingPackage.PackageVersion)
continue;
Debug.WriteLine("Package '{0}' with version {1} in {2} doesn't match with version {3} in {4}",
package.PackageName, package.PackageVersion, package.ConfigFilePath,
matchingPackage.PackageVersion, matchingPackage.ConfigFilePath);
failTest = true;
}
}
Assert.IsFalse(failTest);
}
[Test]
public void NuSpec_File_Matches_Installed_Dependencies()
{
var solutionDirectory = GetSolutionDirectory();
Assert.NotNull(solutionDirectory);
Assert.NotNull(solutionDirectory.Parent);
var nuSpecPath = solutionDirectory.Parent.FullName + "\\build\\NuSpecs\\";
Assert.IsTrue(Directory.Exists(nuSpecPath));
var packagesAndVersions = GetNuGetPackagesInSolution();
var failTest = false;
var nuSpecFiles = Directory.GetFiles(nuSpecPath);
foreach (var fileName in nuSpecFiles.Where(x => x.EndsWith("nuspec")))
{
var serializer = new XmlSerializer(typeof(NuSpec));
using (var reader = new StreamReader(fileName))
{
var nuspec = (NuSpec)serializer.Deserialize(reader);
var dependencies = nuspec.MetaData.dependencies;
//UmbracoCms.Core has version "[$version$]" - ignore
foreach (var dependency in dependencies.Where(x => x.Id != "UmbracoCms.Core"))
{
var dependencyVersionRange = dependency.Version.Replace("[", string.Empty).Replace("(", string.Empty).Split(',');
var dependencyMinimumVersion = dependencyVersionRange.First().Trim();
var matchingPackages = packagesAndVersions.Where(x => string.Equals(x.PackageName, dependency.Id,
StringComparison.InvariantCultureIgnoreCase)).ToList();
if (matchingPackages.Any() == false)
continue;
// NuGet_Package_Versions_Are_The_Same_In_All_Package_Config_Files test
// guarantees that all packages have one version, solutionwide, so it's okay to take First() here
if (dependencyMinimumVersion != matchingPackages.First().PackageVersion)
{
Debug.WriteLine("NuSpec dependency '{0}' with minimum version {1} in doesn't match with version {2} in the solution",
dependency.Id, dependencyMinimumVersion, matchingPackages.First().PackageVersion);
failTest = true;
}
}
}
}
Assert.IsFalse(failTest);
}
private List<PackageVersionMatcher> GetNuGetPackagesInSolution()
{
var packagesAndVersions = new List<PackageVersionMatcher>();
var solutionDirectory = GetSolutionDirectory();
if (solutionDirectory == null)
return packagesAndVersions;
var packageConfigFiles = new List<FileInfo>();
var packageDirectories =
solutionDirectory.GetDirectories().Where(x =>
x.Name.StartsWith("Umbraco.Tests") == false &&
x.Name.StartsWith("Umbraco.MSBuild.Tasks") == false).ToArray();
foreach (var directory in packageDirectories)
{
var packagesFiles = directory.EnumerateFiles("packages.config");
packageConfigFiles.AddRange(packagesFiles);
}
foreach (var file in packageConfigFiles)
{
//read all and de-duplicate packages
var serializer = new XmlSerializer(typeof(PackageConfigEntries));
using (var reader = new StreamReader(file.FullName))
{
var packages = (PackageConfigEntries)serializer.Deserialize(reader);
packagesAndVersions.AddRange(packages.Package.Select(package =>
new PackageVersionMatcher
{
PackageName = package.Id,
PackageVersion = package.Version,
ConfigFilePath = file.FullName
}));
}
}
return packagesAndVersions;
}
private DirectoryInfo GetSolutionDirectory()
{
var testsDirectory = new FileInfo(TestHelper.MapPathForTest("~/"));
if (testsDirectory.Directory == null)
return null;
if (testsDirectory.Directory.Parent == null || testsDirectory.Directory.Parent.Parent == null)
return null;
var solutionDirectory = testsDirectory.Directory.Parent.Parent.Parent;
return solutionDirectory;
}
}
public class PackageVersionMatcher
{
public string ConfigFilePath { get; set; }
public string PackageName { get; set; }
public string PackageVersion { get; set; }
}
[XmlType(AnonymousType = true)]
[XmlRoot(Namespace = "", IsNullable = false, ElementName = "packages")]
public class PackageConfigEntries
{
[XmlElement("package")]
public PackagesPackage[] Package { get; set; }
}
[XmlType(AnonymousType = true, TypeName = "package")]
public class PackagesPackage
{
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
[XmlAttribute(AttributeName = "version")]
public string Version { get; set; }
}
[XmlType(AnonymousType = true, Namespace = "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd")]
[XmlRoot(Namespace = "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd", IsNullable = false, ElementName = "package")]
public class NuSpec
{
[XmlElement("metadata")]
public Metadata MetaData { get; set; }
}
[XmlType(AnonymousType = true, Namespace = "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd", TypeName = "metadata")]
public class Metadata
{
[XmlArrayItem("dependency", IsNullable = false)]
//TODO: breaks when renamed to capital D
public Dependency[] dependencies { get; set; }
}
[XmlType(AnonymousType = true, Namespace = "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd", TypeName = "dependencies")]
public class Dependency
{
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
[XmlAttribute(AttributeName = "version")]
public string Version { get; set; }
}
}
@@ -67,6 +67,66 @@ namespace Umbraco.Tests.Persistence.Repositories
return repository;
}
/// <summary>
/// This tests the regression issue of U4-9438
/// </summary>
/// <remarks>
/// The problem was the iteration of the property data in VersionableRepositoryBase when a content item
/// in the list actually doesn't have any property types, it would still skip over a property row.
/// To test, we have 3 content items, the first has properties, the second doesn't and the third does.
/// </remarks>
[Test]
public void Property_Data_Assigned_Correctly()
{
// Arrange
var provider = new PetaPocoUnitOfWorkProvider(Logger);
var unitOfWork = provider.GetUnitOfWork();
ContentTypeRepository contentTypeRepository;
var allContent = new List<IContent>();
using (var repository = CreateRepository(unitOfWork, out contentTypeRepository))
{
var emptyContentType = MockedContentTypes.CreateBasicContentType();
var hasPropertiesContentType = MockedContentTypes.CreateSimpleContentType("umbTextpage1", "Textpage");
var content1 = MockedContent.CreateSimpleContent(hasPropertiesContentType);
var content2 = MockedContent.CreateBasicContent(emptyContentType);
var content3 = MockedContent.CreateSimpleContent(hasPropertiesContentType);
// Act
contentTypeRepository.AddOrUpdate(emptyContentType);
contentTypeRepository.AddOrUpdate(hasPropertiesContentType);
repository.AddOrUpdate(content1);
repository.AddOrUpdate(content2);
repository.AddOrUpdate(content3);
unitOfWork.Commit();
allContent.Add(content1);
allContent.Add(content2);
allContent.Add(content3);
}
// Assert
using (var repository = CreateRepository(unitOfWork, out contentTypeRepository))
{
//this will cause the GetPropertyCollection to execute and we need to ensure that
// all of the properties and property types are all correct
var result = repository.GetAll(allContent.Select(x => x.Id).ToArray()).ToArray();
foreach (var content in result)
{
foreach (var contentProperty in content.Properties)
{
//prior to the fix, the 2nd document iteration in the GetPropertyCollection would have caused
//the enumerator to move forward past the first property of the 3rd document which would have
//ended up not assiging a property to the 3rd document. This would have ended up with the 3rd
//document still having 3 properties but the last one would not have been assigned an identity
//because the property data would not have been assigned.
Assert.IsTrue(contentProperty.HasIdentity);
}
}
}
}
[Test]
public void Rebuild_Xml_Structures_With_Non_Latest_Version()
{
@@ -172,6 +172,38 @@ namespace Umbraco.Tests.Persistence.Repositories
}
}
[Test]
public void Can_Get_All_Containers()
{
var provider = new PetaPocoUnitOfWorkProvider(Logger);
var unitOfWork = provider.GetUnitOfWork();
EntityContainer container1, container2, container3;
using (var containerRepository = CreateContainerRepository(unitOfWork, Constants.ObjectTypes.DocumentTypeContainerGuid))
{
container1 = new EntityContainer(Constants.ObjectTypes.DocumentTypeGuid) { Name = "container1" };
containerRepository.AddOrUpdate(container1);
container2 = new EntityContainer(Constants.ObjectTypes.DocumentTypeGuid) { Name = "container2" };
containerRepository.AddOrUpdate(container2);
container3 = new EntityContainer(Constants.ObjectTypes.DocumentTypeGuid) { Name = "container3" };
containerRepository.AddOrUpdate(container3);
unitOfWork.Commit();
Assert.That(container1.Id, Is.GreaterThan(0));
Assert.That(container2.Id, Is.GreaterThan(0));
Assert.That(container3.Id, Is.GreaterThan(0));
}
using (var containerRepository = CreateContainerRepository(unitOfWork, Constants.ObjectTypes.DocumentTypeContainerGuid))
{
var found1 = containerRepository.Get(container1.Id);
Assert.IsNotNull(found1);
var found2 = containerRepository.Get(container2.Id);
Assert.IsNotNull(found2);
var found3 = containerRepository.Get(container3.Id);
Assert.IsNotNull(found3);
var allContainers = containerRepository.GetAll();
Assert.AreEqual(3, allContainers.Count());
}
}
[Test]
public void Can_Delete_Container()
{
@@ -1,6 +1,8 @@
using System;
using System.Linq;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.Store;
using Moq;
using NUnit.Framework;
@@ -31,12 +33,15 @@ namespace Umbraco.Tests.PublishedContent
[Test]
public void Ensure_Children_Are_Sorted()
{
using (var luceneDir = new RAMDirectory())
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
{
var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
//var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
indexer.RebuildIndex();
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
//var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var result = searcher.Search(searcher.CreateSearchCriteria().Id(1111).Compile());
Assert.IsNotNull(result);
Assert.AreEqual(1, result.TotalItemCount);
@@ -60,12 +65,15 @@ namespace Umbraco.Tests.PublishedContent
[Test]
public void Ensure_Result_Has_All_Values()
{
using (var luceneDir = new RAMDirectory())
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
{
var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
//var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
indexer.RebuildIndex();
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
//var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var result = searcher.Search(searcher.CreateSearchCriteria().Id(1111).Compile());
Assert.IsNotNull(result);
Assert.AreEqual(1, result.TotalItemCount);
@@ -27,6 +27,7 @@ using UmbracoExamine;
using UmbracoExamine.DataServices;
using umbraco.BusinessLogic;
using System.Linq;
using Lucene.Net.Index;
namespace Umbraco.Tests.PublishedContent
{
@@ -106,11 +107,14 @@ namespace Umbraco.Tests.PublishedContent
[Test]
public void Ensure_Children_Sorted_With_Examine()
{
using (var luceneDir = new RAMDirectory())
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
{
var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
//var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
indexer.RebuildIndex();
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
//var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var ctx = GetUmbracoContext("/test", 1234);
var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx);
@@ -135,11 +139,14 @@ namespace Umbraco.Tests.PublishedContent
[Test]
public void Do_Not_Find_In_Recycle_Bin()
{
using (var luceneDir = new RAMDirectory())
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
{
var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
//var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
indexer.RebuildIndex();
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
//var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var ctx = GetUmbracoContext("/test", 1234);
var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx);
@@ -175,11 +182,14 @@ namespace Umbraco.Tests.PublishedContent
[Test]
public void Children_With_Examine()
{
using (var luceneDir = new RAMDirectory())
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
{
var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
//var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
indexer.RebuildIndex();
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
//var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var ctx = GetUmbracoContext("/test", 1234);
var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx);
@@ -197,11 +207,14 @@ namespace Umbraco.Tests.PublishedContent
[Test]
public void Descendants_With_Examine()
{
using (var luceneDir = new RAMDirectory())
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
{
var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
//var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
indexer.RebuildIndex();
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
//var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var ctx = GetUmbracoContext("/test", 1234);
var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx);
@@ -219,11 +232,14 @@ namespace Umbraco.Tests.PublishedContent
[Test]
public void DescendantsOrSelf_With_Examine()
{
using (var luceneDir = new RAMDirectory())
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
{
var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
//var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
indexer.RebuildIndex();
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
//var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var ctx = GetUmbracoContext("/test", 1234);
var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx);
@@ -241,12 +257,15 @@ namespace Umbraco.Tests.PublishedContent
[Test]
public void Ancestors_With_Examine()
{
using (var luceneDir = new RAMDirectory())
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
{
var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
//var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
indexer.RebuildIndex();
var ctx = GetUmbracoContext("/test", 1234);
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
//var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx);
//we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace
@@ -260,12 +279,15 @@ namespace Umbraco.Tests.PublishedContent
[Test]
public void AncestorsOrSelf_With_Examine()
{
using (var luceneDir = new RAMDirectory())
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
{
var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
//var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
indexer.RebuildIndex();
var ctx = GetUmbracoContext("/test", 1234);
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
//var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx);
//we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace
@@ -668,6 +668,17 @@ namespace Umbraco.Tests.Services
Assert.Throws<Exception>(() => contentService.CreateContent("Test", -1, "umbAliasDoesntExist"));
}
[Test]
public void Cannot_Save_Content_With_Empty_Name()
{
// Arrange
var contentService = ServiceContext.ContentService;
var content = new Content(string.Empty, -1, ServiceContext.ContentTypeService.GetContentType("umbTextpage"));
// Act & Assert
Assert.Throws<ArgumentException>(() => contentService.Save(content));
}
[Test]
public void Can_Get_Content_By_Id()
{
@@ -1328,6 +1339,69 @@ namespace Umbraco.Tests.Services
Assert.That(contents.Any(), Is.False);
}
[Test]
public void Can_Empty_RecycleBin_With_Content_That_Has_All_Related_Data()
{
// Arrange
//need to:
// * add relations
// * add permissions
// * add notifications
// * public access
// * tags
// * domain
// * published & preview data
// * multiple versions
var contentType = MockedContentTypes.CreateAllTypesContentType("test", "test");
ServiceContext.ContentTypeService.Save(contentType, 0);
object obj =
new
{
tags = "Hello,World"
};
var content1 = MockedContent.CreateBasicContent(contentType);
content1.PropertyValues(obj);
content1.ResetDirtyProperties(false);
ServiceContext.ContentService.Save(content1, 0);
Assert.IsTrue(ServiceContext.ContentService.PublishWithStatus(content1, 0).Success);
var content2 = MockedContent.CreateBasicContent(contentType);
content2.PropertyValues(obj);
content2.ResetDirtyProperties(false);
ServiceContext.ContentService.Save(content2, 0);
Assert.IsTrue(ServiceContext.ContentService.PublishWithStatus(content2, 0).Success);
ServiceContext.RelationService.Save(new RelationType(Constants.ObjectTypes.DocumentGuid, Constants.ObjectTypes.DocumentGuid, "test"));
Assert.IsNotNull(ServiceContext.RelationService.Relate(content1, content2, "test"));
ServiceContext.PublicAccessService.Save(new PublicAccessEntry(content1, content2, content2, new List<PublicAccessRule>
{
new PublicAccessRule
{
RuleType = "test",
RuleValue = "test"
}
}));
Assert.IsTrue(ServiceContext.PublicAccessService.AddRule(content1, "test2", "test2").Success);
Assert.IsNotNull(ServiceContext.NotificationService.CreateNotification(ServiceContext.UserService.GetUserById(0), content1, "test"));
ServiceContext.ContentService.AssignContentPermission(content1, 'A', new[] {0});
Assert.IsTrue(ServiceContext.DomainService.Save(new UmbracoDomain("www.test.com", "en-AU")
{
RootContentId = content1.Id
}).Success);
// Act
ServiceContext.ContentService.EmptyRecycleBin();
var contents = ServiceContext.ContentService.GetContentInRecycleBin();
// Assert
Assert.That(contents.Any(), Is.False);
}
[Test]
public void Can_Move_Content()
{
@@ -304,6 +304,42 @@ namespace Umbraco.Tests.Services
Assert.IsNull(deletedContentType);
}
[Test]
public void Can_Create_Container()
{
// Arrange
var cts = ServiceContext.ContentTypeService;
// Act
var container = new EntityContainer(Constants.ObjectTypes.DocumentTypeGuid);
container.Name = "container1";
cts.SaveContentTypeContainer(container);
// Assert
var createdContainer = cts.GetContentTypeContainer(container.Id);
Assert.IsNotNull(createdContainer);
}
[Test]
public void Can_Get_All_Containers()
{
// Arrange
var cts = ServiceContext.ContentTypeService;
// Act
var container1 = new EntityContainer(Constants.ObjectTypes.DocumentTypeGuid);
container1.Name = "container1";
cts.SaveContentTypeContainer(container1);
var container2 = new EntityContainer(Constants.ObjectTypes.DocumentTypeGuid);
container2.Name = "container2";
cts.SaveContentTypeContainer(container2);
// Assert
var containers = cts.GetContentTypeContainers(new int[0]);
Assert.AreEqual(2, containers.Count());
}
[Test]
public void Deleting_ContentType_Sends_Correct_Number_Of_DeletedEntities_In_Events()
{
@@ -717,6 +753,16 @@ namespace Umbraco.Tests.Services
Assert.DoesNotThrow(() => service.GetContentType("advancedPage"));
}
[Test]
public void Cannot_Save_ContentType_With_Empty_Name()
{
// Arrange
var contentType = MockedContentTypes.CreateSimpleContentType("contentType", string.Empty);
// Act & Assert
Assert.Throws<ArgumentException>(() => ServiceContext.ContentTypeService.Save(contentType));
}
[Test]
public void Cannot_Rename_PropertyType_Alias_On_Composition_Which_Would_Cause_Conflict_In_Other_Composition()
{
@@ -221,5 +221,18 @@ namespace Umbraco.Tests.Services
Assert.AreEqual("preVal1", preVals.PreValuesAsArray.First().Value);
Assert.AreEqual("preVal2", preVals.PreValuesAsArray.Last().Value);
}
[Test]
public void Cannot_Save_DataType_With_Empty_Name()
{
// Arrange
var dataTypeService = ServiceContext.DataTypeService;
// Act
var dataTypeDefinition = new DataTypeDefinition(-1, "Test.TestEditor") { Name = string.Empty, DatabaseType = DataTypeDatabaseType.Ntext };
// Act & Assert
Assert.Throws<ArgumentException>(() => dataTypeService.Save(dataTypeDefinition));
}
}
}
@@ -1,4 +1,5 @@
using System.Linq;
using System;
using System.Linq;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
@@ -217,6 +218,17 @@ namespace Umbraco.Tests.Services
}
[Test]
public void Cannot_Save_Macro_With_Empty_Name()
{
// Arrange
var macroService = ServiceContext.MacroService;
var macro = new Macro("test", string.Empty, scriptPath: "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234);
// Act & Assert
Assert.Throws<ArgumentException>(() => macroService.Save(macro));
}
//[Test]
//public void Can_Get_Many_By_Alias()
//{
@@ -81,6 +81,19 @@ namespace Umbraco.Tests.Services
Assert.That(mediaChild.Trashed, Is.False);
}
[Test]
public void Cannot_Save_Media_With_Empty_Name()
{
// Arrange
var mediaService = ServiceContext.MediaService;
var mediaType = MockedContentTypes.CreateVideoMediaType();
ServiceContext.ContentTypeService.Save(mediaType);
var media = mediaService.CreateMedia(string.Empty, -1, "video");
// Act & Assert
Assert.Throws<ArgumentException>(() => mediaService.Save(media));
}
[Test]
public void Ensure_Content_Xml_Created()
{
@@ -185,6 +185,18 @@ namespace Umbraco.Tests.Services
Assert.AreEqual(2, membersInRole.Count());
}
[Test]
public void Cannot_Save_Member_With_Empty_Name()
{
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType();
ServiceContext.MemberTypeService.Save(memberType);
IMember member = MockedMember.CreateSimpleMember(memberType, string.Empty, "test@test.com", "pass", "test");
// Act & Assert
Assert.Throws<ArgumentException>(() => ServiceContext.MemberService.Save(member));
}
[TestCase("MyTestRole1", "test1", StringPropertyMatchType.StartsWith, 1)]
[TestCase("MyTestRole1", "test", StringPropertyMatchType.StartsWith, 3)]
[TestCase("MyTestRole1", "test1", StringPropertyMatchType.Exact, 1)]
@@ -173,6 +173,16 @@ namespace Umbraco.Tests.Services
}
}
[Test]
public void Cannot_Save_MemberType_With_Empty_Name()
{
// Arrange
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType("memberTypeAlias", string.Empty);
// Act & Assert
Assert.Throws<ArgumentException>(() => ServiceContext.MemberTypeService.Save(memberType));
}
//[Test]
//public void Can_Save_MemberType_Structure_And_Create_A_Member_Based_On_It()
//{
@@ -490,6 +490,43 @@ namespace Umbraco.Tests.Services
Assert.IsTrue(result4.AllowedSections.Contains("test"));
}
[Test]
public void Cannot_Create_User_With_Empty_Username()
{
// Arrange
var userService = ServiceContext.UserService;
var userType = userService.GetUserTypeByAlias("admin");
// Act & Assert
Assert.Throws<ArgumentException>(() => userService.CreateUserWithIdentity(string.Empty, "john@umbraco.io", userType));
}
[Test]
public void Cannot_Save_User_With_Empty_Username()
{
// Arrange
var userService = ServiceContext.UserService;
var userType = userService.GetUserTypeByAlias("admin");
var user = userService.CreateUserWithIdentity("John Doe", "john@umbraco.io", userType);
user.Username = string.Empty;
// Act & Assert
Assert.Throws<ArgumentException>(() => userService.Save(user));
}
[Test]
public void Cannot_Save_User_With_Empty_Name()
{
// Arrange
var userService = ServiceContext.UserService;
var userType = userService.GetUserTypeByAlias("admin");
var user = userService.CreateUserWithIdentity("John Doe", "john@umbraco.io", userType);
user.Name = string.Empty;
// Act & Assert
Assert.Throws<ArgumentException>(() => userService.Save(user));
}
[Test]
public void Get_By_Profile_Username()
{
@@ -6,9 +6,18 @@ namespace Umbraco.Tests.TestHelpers.Entities
{
public class MockedContent
{
public static Content CreateBasicContent(IContentType contentType)
{
var content = new Content("Home", -1, contentType) { Level = 1, SortOrder = 1, CreatorId = 0, WriterId = 0 };
content.ResetDirtyProperties(false);
return content;
}
public static Content CreateSimpleContent(IContentType contentType)
{
var content = new Content("Home", -1, contentType) { Language = "en-US", Level = 1, SortOrder = 1, CreatorId = 0, WriterId = 0 };
var content = new Content("Home", -1, contentType) { Level = 1, SortOrder = 1, CreatorId = 0, WriterId = 0 };
object obj =
new
{
@@ -26,7 +35,7 @@ namespace Umbraco.Tests.TestHelpers.Entities
public static Content CreateSimpleContent(IContentType contentType, string name, int parentId)
{
var content = new Content(name, parentId, contentType) { Language = "en-US", CreatorId = 0, WriterId = 0 };
var content = new Content(name, parentId, contentType) { CreatorId = 0, WriterId = 0 };
object obj =
new
{
@@ -44,7 +53,7 @@ namespace Umbraco.Tests.TestHelpers.Entities
public static Content CreateSimpleContent(IContentType contentType, string name, IContent parent)
{
var content = new Content(name, parent, contentType) { Language = "en-US", CreatorId = 0, WriterId = 0 };
var content = new Content(name, parent, contentType) { CreatorId = 0, WriterId = 0 };
object obj =
new
{
@@ -62,7 +71,7 @@ namespace Umbraco.Tests.TestHelpers.Entities
public static Content CreateTextpageContent(IContentType contentType, string name, int parentId)
{
var content = new Content(name, parentId, contentType) { Language = "en-US", CreatorId = 0, WriterId = 0};
var content = new Content(name, parentId, contentType) { CreatorId = 0, WriterId = 0};
object obj =
new
{
@@ -81,7 +90,7 @@ namespace Umbraco.Tests.TestHelpers.Entities
public static Content CreateSimpleContentWithSpecialDatabaseTypes(IContentType contentType, string name, int parentId, string decimalValue, string intValue, DateTime datetimeValue)
{
var content = new Content(name, parentId, contentType) { Language = "en-US", CreatorId = 0, WriterId = 0 };
var content = new Content(name, parentId, contentType) { CreatorId = 0, WriterId = 0 };
object obj = new
{
decimalProperty = decimalValue,
@@ -96,7 +105,7 @@ namespace Umbraco.Tests.TestHelpers.Entities
public static Content CreateAllTypesContent(IContentType contentType, string name, int parentId)
{
var content = new Content("Random Content Name", parentId, contentType) { Language = "en-US", Level = 1, SortOrder = 1, CreatorId = 0, WriterId = 0 };
var content = new Content("Random Content Name", parentId, contentType) { Level = 1, SortOrder = 1, CreatorId = 0, WriterId = 0 };
content.SetValue("isTrue", true);
content.SetValue("number", 42);
@@ -130,7 +139,7 @@ namespace Umbraco.Tests.TestHelpers.Entities
for (int i = 0; i < amount; i++)
{
var name = "Textpage No-" + i;
var content = new Content(name, parentId, contentType) { Language = "en-US", CreatorId = 0, WriterId = 0 };
var content = new Content(name, parentId, contentType) { CreatorId = 0, WriterId = 0 };
object obj =
new
{
@@ -7,6 +7,13 @@ namespace Umbraco.Tests.TestHelpers.Entities
{
public class MockedContentTypes
{
/// <summary>
/// Creates a content type without any properties
/// </summary>
/// <param name="alias"></param>
/// <param name="name"></param>
/// <param name="parent"></param>
/// <returns></returns>
public static ContentType CreateBasicContentType(string alias = "basePage", string name = "Base Page",
ContentType parent = null)
{
+5 -2
View File
@@ -57,8 +57,8 @@
<Reference Include="AutoMapper.Net4, Version=3.3.1.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005, processorArchitecture=MSIL">
<HintPath>..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.Net4.dll</HintPath>
</Reference>
<Reference Include="Examine, Version=0.1.70.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Examine.0.1.70.0\lib\Examine.dll</HintPath>
<Reference Include="Examine, Version=0.1.81.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Examine.0.1.81\lib\net45\Examine.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
<HintPath>..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll</HintPath>
@@ -154,6 +154,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Dependencies\NuGet.cs" />
<Compile Include="Migrations\MigrationIssuesTests.cs" />
<Compile Include="Persistence\BulkDataReaderTests.cs" />
<Compile Include="Persistence\Migrations\MigrationStartupHandlerTests.cs" />
@@ -163,6 +164,8 @@
<Compile Include="Routing\NiceUrlRoutesTests.cs" />
<Compile Include="TestHelpers\Entities\MockedPropertyTypes.cs" />
<Compile Include="TryConvertToTests.cs" />
<Compile Include="UmbracoExamine\RandomIdRAMDirectory.cs" />
<Compile Include="UmbracoExamine\UmbracoContentIndexerTests.cs" />
<Compile Include="Web\AngularIntegration\AngularAntiForgeryTests.cs" />
<Compile Include="Web\AngularIntegration\ContentModelSerializationTests.cs" />
<Compile Include="Web\AngularIntegration\JsInitializationTests.cs" />
+25 -37
View File
@@ -1,6 +1,8 @@
using System;
using System.Linq;
using Examine;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Index;
using Lucene.Net.Store;
using NUnit.Framework;
using Umbraco.Tests.TestHelpers;
@@ -15,53 +17,39 @@ namespace Umbraco.Tests.UmbracoExamine
[Test]
public void Events_Ignoring_Node()
{
//change the parent id so that they are all ignored
var existingCriteria = _indexer.IndexerData;
_indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
999); //change to 999
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
{
//change the parent id so that they are all ignored
var existingCriteria = indexer.IndexerData;
indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
999); //change to 999
var isIgnored = false;
var isIgnored = false;
EventHandler<IndexingNodeDataEventArgs> ignoringNode = (s, e) =>
{
isIgnored = true;
};
EventHandler<IndexingNodeDataEventArgs> ignoringNode = (s, e) =>
{
isIgnored = true;
};
_indexer.IgnoringNode += ignoringNode;
indexer.IgnoringNode += ignoringNode;
//get a node from the data repo
var node = _contentService.GetPublishedContentByXPath("//*[string-length(@id)>0 and number(@id)>0]")
.Root
.Elements()
.First();
//get a node from the data repo
var node = _contentService.GetPublishedContentByXPath("//*[string-length(@id)>0 and number(@id)>0]")
.Root
.Elements()
.First();
_indexer.ReIndexNode(node, IndexTypes.Content);
indexer.ReIndexNode(node, IndexTypes.Content);
Assert.IsTrue(isIgnored);
Assert.IsTrue(isIgnored);
}
}
private readonly TestContentService _contentService = new TestContentService();
private static UmbracoExamineSearcher _searcher;
private static UmbracoContentIndexer _indexer;
private Lucene.Net.Store.Directory _luceneDir;
public override void Initialize()
{
base.Initialize();
_luceneDir = new RAMDirectory();
_indexer = IndexInitializer.GetUmbracoIndexer(_luceneDir);
_indexer.RebuildIndex();
_searcher = IndexInitializer.GetUmbracoSearcher(_luceneDir);
}
public override void TearDown()
{
base.TearDown();
_luceneDir.Dispose();
}
}
}
@@ -7,6 +7,7 @@ using Examine.LuceneEngine.Config;
using Examine.LuceneEngine.Providers;
using Lucene.Net.Analysis;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Index;
using Lucene.Net.Store;
using Moq;
using Umbraco.Core;
@@ -33,7 +34,7 @@ namespace Umbraco.Tests.UmbracoExamine
internal static class IndexInitializer
{
public static UmbracoContentIndexer GetUmbracoIndexer(
Directory luceneDir,
IndexWriter writer,
Analyzer analyzer = null,
IDataService dataService = null,
IContentService contentService = null,
@@ -41,7 +42,8 @@ namespace Umbraco.Tests.UmbracoExamine
IDataTypeService dataTypeService = null,
IMemberService memberService = null,
IUserService userService = null,
IContentTypeService contentTypeService = null)
IContentTypeService contentTypeService = null,
bool supportUnpublishedContent = false)
{
if (dataService == null)
{
@@ -177,15 +179,17 @@ namespace Umbraco.Tests.UmbracoExamine
var indexCriteria = indexSet.ToIndexCriteria(dataService, UmbracoContentIndexer.IndexFieldPolicies);
var i = new UmbracoContentIndexer(indexCriteria,
luceneDir, //custom lucene directory
dataService,
contentService,
mediaService,
dataTypeService,
userService,
contentTypeService,
analyzer,
false);
writer,
dataService,
contentService,
mediaService,
dataTypeService,
userService,
contentTypeService,
false)
{
SupportUnpublishedContent = supportUnpublishedContent
};
//i.IndexSecondsInterval = 1;
@@ -193,13 +197,14 @@ namespace Umbraco.Tests.UmbracoExamine
return i;
}
public static UmbracoExamineSearcher GetUmbracoSearcher(Directory luceneDir, Analyzer analyzer = null)
public static UmbracoExamineSearcher GetUmbracoSearcher(IndexWriter writer, Analyzer analyzer = null)
{
if (analyzer == null)
{
analyzer = new StandardAnalyzer(Version.LUCENE_29);
}
return new UmbracoExamineSearcher(luceneDir, analyzer);
return new UmbracoExamineSearcher(writer, analyzer);
}
public static LuceneSearcher GetLuceneSearcher(Directory luceneDir)
+181 -169
View File
@@ -1,11 +1,11 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using Examine;
using Examine.LuceneEngine;
using Examine.LuceneEngine.Providers;
using Examine.LuceneEngine.SearchCriteria;
using Examine.SearchCriteria;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Index;
using Lucene.Net.Search;
using Lucene.Net.Store;
@@ -15,218 +15,230 @@ using UmbracoExamine;
namespace Umbraco.Tests.UmbracoExamine
{
/// <summary>
/// Tests the standard indexing capabilities
/// </summary>
[DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)]
[TestFixture, RequiresSTA]
public class IndexTest : ExamineBaseTest
{
{
/// <summary>
/// Check that the node signalled as protected in the content service is not present in the index.
/// </summary>
[Test]
public void Index_Protected_Content_Not_Indexed()
{
///// <summary>
/// <summary>
/// Check that the node signalled as protected in the content service is not present in the index.
/// </summary>
[Test]
public void Index_Protected_Content_Not_Indexed()
{
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
{
indexer.RebuildIndex();
var protectedQuery = new BooleanQuery();
protectedQuery.Add(
new BooleanClause(
new TermQuery(new Term(LuceneIndexer.IndexTypeFieldName, IndexTypes.Content)),
BooleanClause.Occur.MUST));
var protectedQuery = new BooleanQuery();
protectedQuery.Add(
new BooleanClause(
new TermQuery(new Term(LuceneIndexer.IndexTypeFieldName, IndexTypes.Content)),
BooleanClause.Occur.MUST));
protectedQuery.Add(
new BooleanClause(
new TermQuery(new Term(LuceneIndexer.IndexNodeIdFieldName, TestContentService.ProtectedNode.ToString())),
BooleanClause.Occur.MUST));
protectedQuery.Add(
new BooleanClause(
new TermQuery(new Term(LuceneIndexer.IndexNodeIdFieldName, TestContentService.ProtectedNode.ToString())),
BooleanClause.Occur.MUST));
var collector = new AllHitsCollector(false, true);
var s = _searcher.GetSearcher();
s.Search(protectedQuery, collector);
var collector = new AllHitsCollector(false, true);
var s = searcher.GetSearcher();
s.Search(protectedQuery, collector);
Assert.AreEqual(0, collector.Count, "Protected node should not be indexed");
Assert.AreEqual(0, collector.Count, "Protected node should not be indexed");
}
}
}
[Test]
public void Index_Move_Media_From_Non_Indexable_To_Indexable_ParentID()
{
//change parent id to 1116
var existingCriteria = _indexer.IndexerData;
_indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
1116);
//rebuild so it excludes children unless they are under 1116
_indexer.RebuildIndex();
[Test]
public void Index_Move_Media_From_Non_Indexable_To_Indexable_ParentID()
{
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
{
indexer.RebuildIndex();
//ensure that node 2112 doesn't exist
var results = _searcher.Search(_searcher.CreateSearchCriteria().Id(2112).Compile());
Assert.AreEqual(0, results.Count());
var mediaService = new TestMediaService();
//get a node from the data repo (this one exists underneath 2222)
var node = _mediaService.GetLatestMediaByXpath("//*[string-length(@id)>0 and number(@id)>0]")
.Root
.Elements()
.Where(x => (int)x.Attribute("id") == 2112)
.First();
//change parent id to 1116
var existingCriteria = indexer.IndexerData;
indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
1116);
var currPath = (string)node.Attribute("path"); //should be : -1,1111,2222,2112
Assert.AreEqual("-1,1111,2222,2112", currPath);
//rebuild so it excludes children unless they are under 1116
indexer.RebuildIndex();
//now mimic moving 2112 to 1116
//node.SetAttributeValue("path", currPath.Replace("2222", "1116"));
node.SetAttributeValue("path", "-1,1116,2112");
node.SetAttributeValue("parentID", "1116");
//ensure that node 2112 doesn't exist
var results = searcher.Search(searcher.CreateSearchCriteria().Id(2112).Compile());
Assert.AreEqual(0, results.TotalItemCount);
//now reindex the node, this should first delete it and then WILL add it because of the parent id constraint
_indexer.ReIndexNode(node, IndexTypes.Media);
//get a node from the data repo (this one exists underneath 2222)
var node = mediaService.GetLatestMediaByXpath("//*[string-length(@id)>0 and number(@id)>0]")
.Root
.Elements()
.First(x => (int)x.Attribute("id") == 2112);
//RESET the parent id
existingCriteria = ((IndexCriteria)_indexer.IndexerData);
_indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
null);
var currPath = (string)node.Attribute("path"); //should be : -1,1111,2222,2112
Assert.AreEqual("-1,1111,2222,2112", currPath);
//now ensure it's deleted
var newResults = _searcher.Search(_searcher.CreateSearchCriteria().Id(2112).Compile());
Assert.AreEqual(1, newResults.Count());
}
//now mimic moving 2112 to 1116
//node.SetAttributeValue("path", currPath.Replace("2222", "1116"));
node.SetAttributeValue("path", "-1,1116,2112");
node.SetAttributeValue("parentID", "1116");
[Test]
[Ignore]
public void Index_Move_Media_To_Non_Indexable_ParentID()
{
//get a node from the data repo (this one exists underneath 2222)
var node = _mediaService.GetLatestMediaByXpath("//*[string-length(@id)>0 and number(@id)>0]")
.Root
.Elements()
.Where(x => (int)x.Attribute("id") == 2112)
.First();
//now reindex the node, this should first delete it and then WILL add it because of the parent id constraint
indexer.ReIndexNode(node, IndexTypes.Media);
var currPath = (string)node.Attribute("path"); //should be : -1,1111,2222,2112
Assert.AreEqual("-1,1111,2222,2112", currPath);
//RESET the parent id
existingCriteria = ((IndexCriteria)indexer.IndexerData);
indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
null);
//ensure it's indexed
_indexer.ReIndexNode(node, IndexTypes.Media);
//now ensure it's deleted
var newResults = searcher.Search(searcher.CreateSearchCriteria().Id(2112).Compile());
Assert.AreEqual(1, newResults.TotalItemCount);
}
//change the parent node id to be the one it used to exist under
var existingCriteria = _indexer.IndexerData;
_indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
2222);
}
//now mimic moving the node underneath 1116 instead of 2222
node.SetAttributeValue("path", currPath.Replace("2222", "1116"));
node.SetAttributeValue("parentID", "1116");
[Test]
public void Index_Move_Media_To_Non_Indexable_ParentID()
{
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
{
indexer.RebuildIndex();
//now reindex the node, this should first delete it and then NOT add it because of the parent id constraint
_indexer.ReIndexNode(node, IndexTypes.Media);
var mediaService = new TestMediaService();
//RESET the parent id
existingCriteria = ((IndexCriteria)_indexer.IndexerData);
_indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
null);
//get a node from the data repo (this one exists underneath 2222)
var node = mediaService.GetLatestMediaByXpath("//*[string-length(@id)>0 and number(@id)>0]")
.Root
.Elements()
.First(x => (int)x.Attribute("id") == 2112);
//now ensure it's deleted
var results = _searcher.Search(_searcher.CreateSearchCriteria().Id(2112).Compile());
Assert.AreEqual(0, results.Count());
var currPath = (string)node.Attribute("path"); //should be : -1,1111,2222,2112
Assert.AreEqual("-1,1111,2222,2112", currPath);
}
//ensure it's indexed
indexer.ReIndexNode(node, IndexTypes.Media);
//change the parent node id to be the one it used to exist under
var existingCriteria = indexer.IndexerData;
indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
2222);
//now mimic moving the node underneath 1116 instead of 2222
node.SetAttributeValue("path", currPath.Replace("2222", "1116"));
node.SetAttributeValue("parentID", "1116");
//now reindex the node, this should first delete it and then NOT add it because of the parent id constraint
indexer.ReIndexNode(node, IndexTypes.Media);
//RESET the parent id
existingCriteria = ((IndexCriteria)indexer.IndexerData);
indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
null);
//now ensure it's deleted
var results = searcher.Search(searcher.CreateSearchCriteria().Id(2112).Compile());
Assert.AreEqual(0, results.TotalItemCount);
}
}
/// <summary>
/// This will ensure that all 'Content' (not media) is cleared from the index using the Lucene API directly.
/// We then call the Examine method to re-index Content and do some comparisons to ensure that it worked correctly.
/// </summary>
[Test]
public void Index_Reindex_Content()
{
var s = (IndexSearcher)_searcher.GetSearcher();
/// <summary>
/// This will ensure that all 'Content' (not media) is cleared from the index using the Lucene API directly.
/// We then call the Examine method to re-index Content and do some comparisons to ensure that it worked correctly.
/// </summary>
[Test]
public void Index_Reindex_Content()
{
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer, supportUnpublishedContent: true))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
{
indexer.RebuildIndex();
//first delete all 'Content' (not media). This is done by directly manipulating the index with the Lucene API, not examine!
var contentTerm = new Term(LuceneIndexer.IndexTypeFieldName, IndexTypes.Content);
var writer = _indexer.GetIndexWriter();
writer.DeleteDocuments(contentTerm);
writer.Commit();
var s = (IndexSearcher)searcher.GetSearcher();
//make sure the content is gone. This is done with lucene APIs, not examine!
var collector = new AllHitsCollector(false, true);
var query = new TermQuery(contentTerm);
s = (IndexSearcher)_searcher.GetSearcher(); //make sure the searcher is up do date.
s.Search(query, collector);
Assert.AreEqual(0, collector.Count);
//first delete all 'Content' (not media). This is done by directly manipulating the index with the Lucene API, not examine!
//call our indexing methods
_indexer.IndexAll(IndexTypes.Content);
var contentTerm = new Term(LuceneIndexer.IndexTypeFieldName, IndexTypes.Content);
writer.DeleteDocuments(contentTerm);
writer.Commit();
collector = new AllHitsCollector(false, true);
s = (IndexSearcher)_searcher.GetSearcher(); //make sure the searcher is up do date.
s.Search(query, collector);
//var ids = new List<string>();
//for (var i = 0; i < collector.Count;i++)
//{
// ids.Add(s.Doc(collector.GetDocId(i)).GetValues("__NodeId")[0]);
//}
Assert.AreEqual(20, collector.Count);
}
//make sure the content is gone. This is done with lucene APIs, not examine!
var collector = new AllHitsCollector(false, true);
var query = new TermQuery(contentTerm);
s = (IndexSearcher)searcher.GetSearcher(); //make sure the searcher is up do date.
s.Search(query, collector);
Assert.AreEqual(0, collector.Count);
/// <summary>
/// This will delete an item from the index and ensure that all children of the node are deleted too!
/// </summary>
[Test]
[Ignore]
public void Index_Delete_Index_Item_Ensure_Heirarchy_Removed()
{
//call our indexing methods
indexer.IndexAll(IndexTypes.Content);
//now delete a node that has children
collector = new AllHitsCollector(false, true);
s = (IndexSearcher)searcher.GetSearcher(); //make sure the searcher is up do date.
s.Search(query, collector);
//var ids = new List<string>();
//for (var i = 0; i < collector.Count;i++)
//{
// ids.Add(s.Doc(collector.GetDocId(i)).GetValues("__NodeId")[0]);
//}
Assert.AreEqual(21, collector.Count);
}
}
_indexer.DeleteFromIndex(1140.ToString());
//this node had children: 1141 & 1142, let's ensure they are also removed
/// <summary>
/// This will delete an item from the index and ensure that all children of the node are deleted too!
/// </summary>
[Test]
public void Index_Delete_Index_Item_Ensure_Heirarchy_Removed()
{
var results = _searcher.Search(_searcher.CreateSearchCriteria().Id(1141).Compile());
Assert.AreEqual(0, results.Count());
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
{
indexer.RebuildIndex();
results = _searcher.Search(_searcher.CreateSearchCriteria().Id(1142).Compile());
Assert.AreEqual(0, results.Count());
//now delete a node that has children
}
indexer.DeleteFromIndex(1140.ToString());
//this node had children: 1141 & 1142, let's ensure they are also removed
#region Private methods and properties
var results = searcher.Search(searcher.CreateSearchCriteria().Id(1141).Compile());
Assert.AreEqual(0, results.TotalItemCount);
private readonly TestContentService _contentService = new TestContentService();
private readonly TestMediaService _mediaService = new TestMediaService();
private static UmbracoExamineSearcher _searcher;
private static UmbracoContentIndexer _indexer;
#endregion
#region Initialize and Cleanup
private Lucene.Net.Store.Directory _luceneDir;
public override void TearDown()
{
base.TearDown();
_luceneDir.Dispose();
results = searcher.Search(searcher.CreateSearchCriteria().Id(1142).Compile());
Assert.AreEqual(0, results.TotalItemCount);
}
}
#region Initialize and Cleanup
public override void TearDown()
{
base.TearDown();
UmbracoExamineSearcher.DisableInitializationCheck = null;
BaseUmbracoIndexer.DisableInitializationCheck = null;
}
public override void Initialize()
{
base.Initialize();
_luceneDir = new RAMDirectory();
_indexer = IndexInitializer.GetUmbracoIndexer(_luceneDir);
_indexer.RebuildIndex();
_searcher = IndexInitializer.GetUmbracoSearcher(_luceneDir);
}
#endregion
}
#endregion
}
}
@@ -0,0 +1,14 @@
using System;
using Lucene.Net.Store;
namespace Umbraco.Tests.UmbracoExamine
{
public class RandomIdRAMDirectory : RAMDirectory
{
private readonly string _lockId = Guid.NewGuid().ToString();
public override string GetLockID()
{
return _lockId;
}
}
}
+13 -31
View File
@@ -8,6 +8,8 @@ using Examine.LuceneEngine.Providers;
using Lucene.Net.Store;
using NUnit.Framework;
using Examine.LuceneEngine.SearchCriteria;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Index;
using Umbraco.Tests.TestHelpers;
namespace Umbraco.Tests.UmbracoExamine
@@ -20,19 +22,17 @@ namespace Umbraco.Tests.UmbracoExamine
[Test]
public void Test_Sort_Order_Sorting()
{
//var newIndexFolder = new DirectoryInfo(Path.Combine("App_Data\\SearchTests", Guid.NewGuid().ToString()));
//System.IO.Directory.CreateDirectory(newIndexFolder.FullName);
using (var luceneDir = new RAMDirectory())
//using (var luceneDir = new SimpleFSDirectory(newIndexFolder))
{
var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir, null,
using (var luceneDir = new RandomIdRAMDirectory())
using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED))
using (var indexer = IndexInitializer.GetUmbracoIndexer(writer, null,
new TestDataService()
{
ContentService = new TestContentService(TestFiles.umbraco_sort)
});
indexer.RebuildIndex();
var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
{
ContentService = new TestContentService(TestFiles.umbraco_sort)
},
supportUnpublishedContent: true))
using (var searcher = IndexInitializer.GetUmbracoSearcher(writer))
{
indexer.RebuildIndex();
var s = (LuceneSearcher)searcher;
var luceneSearcher = s.GetSearcher();
@@ -69,25 +69,7 @@ namespace Umbraco.Tests.UmbracoExamine
currentSort = sort;
}
return true;
}
//[Test]
//public void Test_Index_Type_With_German_Analyzer()
//{
// using (var luceneDir = new RAMDirectory())
// {
// var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir,
// new GermanAnalyzer());
// indexer.RebuildIndex();
// var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
// }
//}
//private readonly TestContentService _contentService = new TestContentService();
//private readonly TestMediaService _mediaService = new TestMediaService();
//private static UmbracoExamineSearcher _searcher;
//private static UmbracoContentIndexer _indexer;
//private Lucene.Net.Store.Directory _luceneDir;
}
}
}
@@ -0,0 +1,107 @@
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Models;
using UmbracoExamine;
namespace Umbraco.Tests.UmbracoExamine
{
[TestFixture]
public class UmbracoContentIndexerTests : ExamineBaseTest
{
[Test]
public void Get_Serialized_Content_No_Published_Content()
{
var contentSet = new List<IContent>
{
Mock.Of<IContent>(c => c.Id == 1 && c.Path == "-1,1" && c.Published && c.Level == 1),
Mock.Of<IContent>(c => c.Id == 2 && c.Path == "-1,2" && c.Published && c.Level == 1),
Mock.Of<IContent>(c => c.Id == 3 && c.Path == "-1,3" && c.Published == false && c.Level == 1), // no
Mock.Of<IContent>(c => c.Id == 4 && c.Path == "-1,4" && c.Published == false && c.Level == 1), // no
Mock.Of<IContent>(c => c.Id == 5 && c.Path == "-1,1,5" && c.Published && c.Level == 2),
Mock.Of<IContent>(c => c.Id == 6 && c.Path == "-1,2,6" && c.Published == false && c.Level == 2), // no
Mock.Of<IContent>(c => c.Id == 7 && c.Path == "-1,3,7" && c.Published && c.Level == 2), // no
Mock.Of<IContent>(c => c.Id == 8 && c.Path == "-1,4,8" && c.Published && c.Level == 2), // no
Mock.Of<IContent>(c => c.Id == 9 && c.Path == "-1,4,9" && c.Published && c.Level == 2), // no
Mock.Of<IContent>(c => c.Id == 10 && c.Path == "-1,1,5,10" && c.Published && c.Level == 3),
Mock.Of<IContent>(c => c.Id == 15 && c.Path == "-1,1,5,15" && c.Published && c.Level == 3),
Mock.Of<IContent>(c => c.Id == 11 && c.Path == "-1,2,6,11" && c.Published && c.Level == 3), // no
Mock.Of<IContent>(c => c.Id == 16 && c.Path == "-1,2,6,16" && c.Published && c.Level == 3), // no
Mock.Of<IContent>(c => c.Id == 12 && c.Path == "-1,3,7,12" && c.Published && c.Level == 3), // no
Mock.Of<IContent>(c => c.Id == 17 && c.Path == "-1,3,7,17" && c.Published && c.Level == 3), // no
Mock.Of<IContent>(c => c.Id == 13 && c.Path == "-1,4,8,13" && c.Published && c.Level == 3), // no
Mock.Of<IContent>(c => c.Id == 18 && c.Path == "-1,4,8,18" && c.Published && c.Level == 3), // no
Mock.Of<IContent>(c => c.Id == 14 && c.Path == "-1,4,9,14" && c.Published && c.Level == 3), // no
Mock.Of<IContent>(c => c.Id == 19 && c.Path == "-1,4,9,19" && c.Published && c.Level == 3), // no
};
//ensure the rest of the required values are populted
foreach (var content in contentSet)
{
var mock = Mock.Get(content);
mock.Setup(x => x.ContentType).Returns(Mock.Of<IContentType>(type => type.Icon == "hello"));
}
contentSet.Sort((a, b) => Comparer<int>.Default.Compare(a.Level, b.Level));
var published = new HashSet<string>();
var result = UmbracoContentIndexer.GetSerializedContent(false, content => new XElement("test"), contentSet, published)
.WhereNotNull()
.ToArray();
Assert.AreEqual(5, result.Length);
}
[Test]
public void Get_Serialized_Content_With_Published_Content()
{
var contentSet = new List<IContent>
{
Mock.Of<IContent>(c => c.Id == 1 && c.Path == "-1,1" && c.Published && c.Level == 1),
Mock.Of<IContent>(c => c.Id == 2 && c.Path == "-1,2" && c.Published && c.Level == 1),
Mock.Of<IContent>(c => c.Id == 3 && c.Path == "-1,3" && c.Published == false && c.Level == 1),
Mock.Of<IContent>(c => c.Id == 4 && c.Path == "-1,4" && c.Published == false && c.Level == 1),
Mock.Of<IContent>(c => c.Id == 5 && c.Path == "-1,1,5" && c.Published && c.Level == 2),
Mock.Of<IContent>(c => c.Id == 6 && c.Path == "-1,2,6" && c.Published == false && c.Level == 2),
Mock.Of<IContent>(c => c.Id == 7 && c.Path == "-1,3,7" && c.Published && c.Level == 2),
Mock.Of<IContent>(c => c.Id == 8 && c.Path == "-1,4,8" && c.Published && c.Level == 2),
Mock.Of<IContent>(c => c.Id == 9 && c.Path == "-1,4,9" && c.Published && c.Level == 2),
Mock.Of<IContent>(c => c.Id == 10 && c.Path == "-1,1,5,10" && c.Published && c.Level == 3),
Mock.Of<IContent>(c => c.Id == 15 && c.Path == "-1,1,5,15" && c.Published && c.Level == 3),
Mock.Of<IContent>(c => c.Id == 11 && c.Path == "-1,2,6,11" && c.Published && c.Level == 3),
Mock.Of<IContent>(c => c.Id == 16 && c.Path == "-1,2,6,16" && c.Published && c.Level == 3),
Mock.Of<IContent>(c => c.Id == 12 && c.Path == "-1,3,7,12" && c.Published && c.Level == 3),
Mock.Of<IContent>(c => c.Id == 17 && c.Path == "-1,3,7,17" && c.Published && c.Level == 3),
Mock.Of<IContent>(c => c.Id == 13 && c.Path == "-1,4,8,13" && c.Published && c.Level == 3),
Mock.Of<IContent>(c => c.Id == 18 && c.Path == "-1,4,8,18" && c.Published && c.Level == 3),
Mock.Of<IContent>(c => c.Id == 14 && c.Path == "-1,4,9,14" && c.Published && c.Level == 3),
Mock.Of<IContent>(c => c.Id == 19 && c.Path == "-1,4,9,19" && c.Published && c.Level == 3),
};
//ensure the rest of the required values are populted
foreach (var content in contentSet)
{
var mock = Mock.Get(content);
mock.Setup(x => x.ContentType).Returns(Mock.Of<IContentType>(type => type.Icon == "hello"));
}
contentSet.Sort((a, b) => Comparer<int>.Default.Compare(a.Level, b.Level));
var published = new HashSet<string>();
var result = UmbracoContentIndexer.GetSerializedContent(true, content => new XElement("test"), contentSet, published)
.WhereNotNull()
.ToArray();
Assert.AreEqual(19, result.Length);
}
}
}
+1 -1
View File
@@ -2,7 +2,7 @@
<packages>
<package id="AspNetWebApi.SelfHost" version="4.0.20710.0" targetFramework="net45" />
<package id="AutoMapper" version="3.3.1" targetFramework="net45" />
<package id="Examine" version="0.1.70.0" targetFramework="net45" />
<package id="Examine" version="0.1.81" targetFramework="net45" />
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net45" />
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net45" />
<package id="Microsoft.AspNet.Mvc" version="5.2.3" targetFramework="net45" />
@@ -204,7 +204,7 @@ angular.module('umbraco.services')
//when it's successful, return the user data
setCurrentUser(data);
var result = { user: data, authenticated: true, lastUserId: lastUserId };
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "credentials" };
//broadcast a global event
eventsService.emit("app.authenticated", result);
@@ -232,7 +232,7 @@ angular.module('umbraco.services')
authResource.getCurrentUser()
.then(function (data) {
var result = { user: data, authenticated: true, lastUserId: lastUserId };
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "implicit" };
//TODO: This is a mega backwards compatibility hack... These variables SHOULD NOT exist in the server variables
// since they are not supposed to be dynamic but I accidentally added them there in 7.1.5 IIRC so some people might
@@ -8,7 +8,7 @@
* The main application controller
*
*/
function MainController($scope, $rootScope, $location, $routeParams, $timeout, $http, $log, appState, treeService, notificationsService, userService, navigationService, historyService, updateChecker, assetsService, eventsService, umbRequestHelper, tmhDynamicLocale) {
function MainController($scope, $rootScope, $location, $routeParams, $timeout, $http, $log, appState, treeService, notificationsService, userService, navigationService, historyService, updateChecker, assetsService, eventsService, umbRequestHelper, tmhDynamicLocale, localStorageService) {
//the null is important because we do an explicit bool check on this in the view
//the avatar is by default the umbraco logo
@@ -81,6 +81,14 @@ function MainController($scope, $rootScope, $location, $routeParams, $timeout, $
$location.path("/").search("");
historyService.removeAll();
treeService.clearCache();
//if the user changed, clearout local storage too - could contain sensitive data
localStorageService.clearAll();
}
//if this is a new login (i.e. the user entered credentials), then clear out local storage - could contain sensitive data
if (data.loginType === "credentials") {
localStorageService.clearAll();
}
//Load locale file
+3 -2
View File
@@ -1,6 +1,6 @@
/** Executed when the application starts, binds to events and set global state */
app.run(['userService', '$log', '$rootScope', '$location', 'navigationService', 'appState', 'editorState', 'fileManager', 'assetsService', 'eventsService', '$cookies', '$templateCache',
function (userService, $log, $rootScope, $location, navigationService, appState, editorState, fileManager, assetsService, eventsService, $cookies, $templateCache) {
app.run(['userService', '$log', '$rootScope', '$location', 'navigationService', 'appState', 'editorState', 'fileManager', 'assetsService', 'eventsService', '$cookies', '$templateCache', 'localStorageService',
function (userService, $log, $rootScope, $location, navigationService, appState, editorState, fileManager, assetsService, eventsService, $cookies, $templateCache, localStorageService) {
//This sets the default jquery ajax headers to include our csrf token, we
// need to user the beforeSend method because our token changes per user/login so
@@ -13,6 +13,7 @@ app.run(['userService', '$log', '$rootScope', '$location', 'navigationService',
/** Listens for authentication and checks if our required assets are loaded, if/once they are we'll broadcast a ready event */
eventsService.on("app.authenticated", function(evt, data) {
assetsService._loadInitAssets().then(function() {
appState.setGlobalState("isReady", true);
@@ -1,7 +1,7 @@
//used for the media picker dialog
angular.module("umbraco")
.controller("Umbraco.Overlays.MediaPickerController",
function($scope, mediaResource, umbRequestHelper, entityResource, $log, mediaHelper, mediaTypeHelper, eventsService, treeService, $element, $timeout, $cookies, $cookieStore, localizationService) {
function ($scope, mediaResource, umbRequestHelper, entityResource, $log, mediaHelper, mediaTypeHelper, eventsService, treeService, $element, $timeout, $cookies, localStorageService, localizationService) {
if (!$scope.model.title) {
$scope.model.title = localizationService.localize("defaultdialogs_selectMedia");
@@ -15,7 +15,7 @@ angular.module("umbraco")
$scope.multiPicker = (dialogOptions.multiPicker && dialogOptions.multiPicker !== "0") ? true : false;
$scope.startNodeId = dialogOptions.startNodeId ? dialogOptions.startNodeId : -1;
$scope.cropSize = dialogOptions.cropSize;
$scope.lastOpenedNode = $cookieStore.get("umbLastOpenedMediaNodeId");
$scope.lastOpenedNode = localStorageService.get("umbLastOpenedMediaNodeId");
if ($scope.onlyImages) {
$scope.acceptedFileTypes = mediaHelper
.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes);
@@ -133,8 +133,7 @@ angular.module("umbraco")
});
$scope.currentFolder = folder;
// for some reason i cannot set cookies with cookieStore
document.cookie = "umbLastOpenedMediaNodeId=" + folder.id;
localStorageService.set("umbLastOpenedMediaNodeId", folder.id);
};
@@ -34,18 +34,24 @@
fields: {},
file: file
}).progress(function (evt) {
// hack: in some browsers the progress event is called after success
// this prevents the UI from going back to a uploading state
if(vm.zipFile.uploadStatus !== "done" && vm.zipFile.uploadStatus !== "error") {
// set view state to uploading
vm.state = 'uploading';
// set view state to uploading
vm.state = 'uploading';
// calculate progress in percentage
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total, 10);
// calculate progress in percentage
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total, 10);
// set percentage property on file
vm.zipFile.uploadProgress = progressPercentage;
// set percentage property on file
vm.zipFile.uploadProgress = progressPercentage;
// set uploading status on file
vm.zipFile.uploadStatus = "uploading";
// set uploading status on file
vm.zipFile.uploadStatus = "uploading";
}
}).success(function (data, status, headers, config) {
@@ -12,8 +12,8 @@
<div class="umb-package-list__item" ng-repeat="installedPackage in vm.installedPackages">
<div class="umb-package-list__item-icon">
<i ng-if="!installedPackage.icon" class="icon-box"></i>
<img ng-if="installedPackage.icon" ng-src="{{installedPackage.icon}}" />
<i ng-if="!installedPackage.iconUrl" class="icon-box"></i>
<img ng-if="installedPackage.iconUrl" ng-src="{{installedPackage.iconUrl}}" />
</div>
<div class="umb-package-list__item-content">
@@ -2,7 +2,7 @@ angular.module("umbraco")
.controller("Umbraco.PropertyEditors.GoogleMapsController",
function ($element, $rootScope, $scope, notificationsService, dialogService, assetsService, $log, $timeout) {
assetsService.loadJs('http://www.google.com/jsapi')
assetsService.loadJs('https://www.google.com/jsapi')
.then(function () {
google.load("maps", "3",
{
+12 -10
View File
@@ -127,19 +127,20 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\dotless.1.4.1.0\lib\dotless.Core.dll</HintPath>
</Reference>
<Reference Include="Examine, Version=0.1.70.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Examine.0.1.70.0\lib\Examine.dll</HintPath>
<Private>True</Private>
<Reference Include="Examine, Version=0.1.81.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Examine.0.1.81\lib\net45\Examine.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll</HintPath>
</Reference>
<Reference Include="ImageProcessor, Version=2.5.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ImageProcessor.2.5.1\lib\net45\ImageProcessor.dll</HintPath>
<Reference Include="ImageProcessor, Version=2.5.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ImageProcessor.2.5.2\lib\net45\ImageProcessor.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ImageProcessor.Web, Version=4.7.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ImageProcessor.Web.4.7.2\lib\net45\ImageProcessor.Web.dll</HintPath>
<Reference Include="ImageProcessor.Web, Version=4.8.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ImageProcessor.Web.4.8.2\lib\net45\ImageProcessor.Web.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="log4net, Version=1.2.11.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
@@ -167,7 +168,8 @@
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.IO.RecyclableMemoryStream, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.IO.RecyclableMemoryStream.1.2.0\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll</HintPath>
<HintPath>..\packages\Microsoft.IO.RecyclableMemoryStream.1.2.1\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Owin">
<HintPath>..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll</HintPath>
@@ -2421,9 +2423,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\"
<WebProjectProperties>
<UseIIS>True</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>7570</DevelopmentServerPort>
<DevelopmentServerPort>7510</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:7570</IISUrl>
<IISUrl>http://localhost:7510</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>
@@ -13,6 +13,7 @@
<umb:JsInclude ID="JsInclude1" runat="server" FilePath="Application/NamespaceManager.js" PathNameAlias="UmbracoClient" Priority="0" />
<umb:JsInclude ID="JsInclude3" runat="server" FilePath="ui/jquery.js" PathNameAlias="UmbracoClient" Priority="1" />
<umb:JsInclude ID="JsInclude4" runat="server" FilePath="lib/jquery-migrate/jquery-migrate.min.js" PathNameAlias="UmbracoRoot" Priority="1" />
<umb:JsInclude ID="JsInclude6" runat="server" FilePath="ui/base2.js" PathNameAlias="UmbracoClient" Priority="1" />
<umb:JsInclude ID="JsInclude11" runat="server" FilePath="UI/knockout.js" PathNameAlias="UmbracoClient" Priority="3" />
<umb:JsInclude ID="JsInclude12" runat="server" FilePath="UI/knockout.mapping.js" PathNameAlias="UmbracoClient" Priority="4" />
@@ -48,6 +48,15 @@ namespace Umbraco.Web.UI.Umbraco.Dashboard {
/// </remarks>
protected global::ClientDependency.Core.Controls.JsInclude JsInclude3;
/// <summary>
/// JsInclude4 control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::ClientDependency.Core.Controls.JsInclude JsInclude4;
/// <summary>
/// JsInclude6 control.
/// </summary>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<caching currentCache="DiskCache">
<caches>
<cache name="DiskCache" type="ImageProcessor.Web.Caching.DiskCache, ImageProcessor.Web" maxDays="365" browserMaxDays="7">
<cache name="DiskCache" type="ImageProcessor.Web.Caching.DiskCache, ImageProcessor.Web" maxDays="365" browserMaxDays="7" trimCache="false">
<settings>
<setting key="VirtualCachePath" value="~/app_data/cache" />
</settings>
+4 -4
View File
@@ -4,9 +4,9 @@
<package id="ClientDependency" version="1.9.2" targetFramework="net45" />
<package id="ClientDependency-Mvc5" version="1.8.0.0" targetFramework="net45" />
<package id="dotless" version="1.4.1.0" targetFramework="net45" />
<package id="Examine" version="0.1.70.0" targetFramework="net45" />
<package id="ImageProcessor" version="2.5.1" targetFramework="net45" />
<package id="ImageProcessor.Web" version="4.7.2" targetFramework="net45" />
<package id="Examine" version="0.1.81" targetFramework="net45" />
<package id="ImageProcessor" version="2.5.2" targetFramework="net45" />
<package id="ImageProcessor.Web" version="4.8.2" targetFramework="net45" />
<package id="ImageProcessor.Web.Config" version="2.3.0" targetFramework="net45" />
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net45" />
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net45" />
@@ -22,7 +22,7 @@
<package id="Microsoft.CodeAnalysis.Analyzers" version="1.0.0" targetFramework="net45" />
<package id="Microsoft.CodeAnalysis.Common" version="1.0.0" targetFramework="net45" />
<package id="Microsoft.CodeAnalysis.CSharp" version="1.0.0" targetFramework="net45" />
<package id="Microsoft.IO.RecyclableMemoryStream" version="1.2.0" targetFramework="net45" />
<package id="Microsoft.IO.RecyclableMemoryStream" version="1.2.1" targetFramework="net45" />
<package id="Microsoft.Owin" version="3.0.1" targetFramework="net45" />
<package id="Microsoft.Owin.Host.SystemWeb" version="3.0.1" targetFramework="net45" />
<package id="Microsoft.Owin.Security" version="3.0.1" targetFramework="net45" />
@@ -1142,7 +1142,7 @@ To manage your website, simply open the Umbraco back office and start adding con
<area alias="modelsBuilder">
<key alias="buildingModels">Building models</key>
<key alias="waitingMessage">this can take abit of time, don't worry</key>
<key alias="waitingMessage">this can take a bit of time, don't worry</key>
<key alias="modelsGenerated">Models generated</key>
<key alias="modelsGeneratedError">Models could not be generated</key>
<key alias="modelsExceptionInUlog">Models generation has failed, see exception in U log</key>
+524 -122
View File
@@ -27,6 +27,10 @@
<key alias="unpublish">Depubliceren</key>
<key alias="refreshNode">Nodes opnieuw inladen</key>
<key alias="republish">Herpubliceer de site</key>
<key alias="SetPermissionsForThePage">Stel rechten voor pagina %0% in</key>
<key alias="chooseWhereToMove">Waarheen verplaatsen/</key>
<key alias="toInTheTreeStructureBelow">Naar de onderstaande boomstructuur</key>
<key alias="restore" version="7.3.0">Herstellen</key>
<key alias="rights">Rechten</key>
<key alias="rollback">Vorige versies</key>
<key alias="sendtopublish">Klaar voor publicatie</key>
@@ -64,10 +68,10 @@
<key alias="atViewingFor">Tonen voor</key>
</area>
<area alias="buttons">
<key alias="clearSelection">Selectie ongedaan maken</key>
<key alias="select">Selecteren</key>
<key alias="selectCurrentFolder">Selecteer huidige map</key>
<key alias="somethingElse">Doe iets anders</key>
<key alias="bold">Vet</key>
<key alias="deindent">Paragraaf uitspringen</key>
<key alias="formFieldInsert">Voeg formulierveld in</key>
@@ -89,11 +93,14 @@
<key alias="save">Opslaan</key>
<key alias="saveAndPublish">Opslaan en publiceren</key>
<key alias="saveToPublish">Opslaan en verzenden voor goedkeuring</key>
<key alias="saveListView">Sla list view op</key>
<key alias="showPage">voorbeeld bekijken</key>
<key alias="showPageDisabled">Voorbeeld bekijken is uitgeschakeld omdat er geen template is geselecteerd</key>
<key alias="styleChoose">Stijl kiezen</key>
<key alias="styleShow">Stijlen tonen</key>
<key alias="tableInsert">Tabel invoegen</key>
<key alias="generateModels">Genereer models</key>
<key alias="saveAndGenerateModels">Opslaan en models genereren</key>
</area>
<area alias="changeDocType">
<key alias="changeDocTypeInstruction">Om het documenttype voor de geselecteerde inhoud te wijzigen, selecteert u eerst uit de lijst van geldige types voor deze locatie.</key>
@@ -133,7 +140,8 @@
<key alias="itemChanged">Dit item is gewijzigd na publicatie</key>
<key alias="itemNotPublished">Dit item is niet gepubliceerd</key>
<key alias="lastPublished">Laatst gepubliceerd op</key>
<key alias="listViewNoItems" version="7.1.5">Nog geen items om weer te geven.</key>
<key alias="noItemsToShow">Er zijn geen items om weer te geven</key>
<key alias="listViewNoItems" version="7.1.5">Er zijn geen items om weer te geven.</key>
<key alias="mediatype">Mediatype</key>
<key alias="mediaLinks">Link naar media item(s)</key>
<key alias="membergroup">Ledengroep</key>
@@ -143,7 +151,9 @@
<key alias="nodeName">Pagina Titel</key>
<key alias="otherElements">Eigenschappen</key>
<key alias="parentNotPublished">Dit document is gepubliceerd maar niet zichtbaar omdat de bovenliggende node '%0%' niet gepubliceerd is</key>
<key alias="parentNotPublishedAnomaly">Oeps: dit document is gepubliceerd, maar het is niet in de cache (interne serverfout)</key>
<key alias="parentNotPublishedAnomaly">Dit document is gepubliceerd, maar het is niet in de cache (interne serverfout)</key>
<key alias="getUrlException">Kan de Url niet ophalen</key>
<key alias="routeError">Dit document is gepubliceerd, maar de Url conflicteert met %0%</key>
<key alias="publish">Publiceren</key>
<key alias="publishStatus">Publicatiestatus</key>
<key alias="releaseDate">Publiceren op</key>
@@ -161,23 +171,34 @@
<key alias="uploadClear">Bestand(en) verwijderen</key>
<key alias="urls">Link naar het document</key>
<key alias="memberof">Lid van groep(en)</key>
<key alias="notmemberof">Geen lid van groep(en)</key>
<key alias="notmemberof">Geen lid van groep(en)</key>
<key alias="childItems" version="7.0">Kinderen</key>
<key alias="target" version="7.0">Doel</key>
<key alias="scheduledPublishServerTime">Dit betekend de volgende tijd op de server:</key>
<key alias="scheduledPublishDocumentation"><![CDATA[<a href="https://our.umbraco.org/documentation/Getting-Started/Data/Scheduled-Publishing/#timezones" target="_blank">Wat houd dit in?</a>]]></key>
</area>
<area alias="media">
<key alias="clickToUpload">Klik om te uploaden</key>
<key alias="dropFilesHere">Plaats je bestanden hier...</key>
<key alias="urls">Link naar media</key>
<key alias="orClickHereToUpload">Of klik hier om bestanden te kiezen</key>
<key alias="onlyAllowedFiles">De toegestane bestandtypen zijn</key>
<key alias="disallowedFileType">Dit bestand heeft niet het juiste file-type. Dit bestand kan niet geupload worden.</key>
<key alias="maxFileSize">Max file size is</key>
</area>
<area alias="member">
<key alias="createNewMember">Maak nieuwe member aan</key>
<key alias="allMembers">Alle Members</key>
</area>
<area alias="create">
<key alias="chooseNode">Waar wil je de nieuwe %0% aanmaken?</key>
<key alias="createUnder">Aanmaken onder</key>
<key alias="updateData">Kies een type en een titel</key>
<key alias="noDocumentTypes" version="7.0"><![CDATA[Er zijn geen toegestane documenttypes beschikbaar. Je moet deze inschakelen in de Instellingen sectie onder <strong>"Documenttypes"</ strong>.]]></key>
<key alias="noMediaTypes" version="7.0"><![CDATA[Er zijn geen toegestande mediatypes beschikbaar. Je moet deze in schakelen in de Instellingen sectie onder <strong>"Mediatypes"</strong>.]]></key>
<key alias="documentTypeWithoutTemplate">Document Type zonder template</key>
<key alias="newFolder">Nieuwe folder</key>
<key alias="newDataType">Nieuw data type</key>
</area>
<area alias="dashboard">
<key alias="browser">Open je website</key>
@@ -189,38 +210,38 @@
<key alias="welcome">Welkom</key>
</area>
<area alias="prompt">
<key alias="stay">Stay</key>
<key alias="discardChanges">Discard changes</key>
<key alias="unsavedChanges">You have unsaved changes</key>
<key alias="unsavedChangesWarning">Are you sure you want to navigate away from this page? - you have unsaved changes</key>
<key alias="stay">Blijf op deze pagina</key>
<key alias="discardChanges">Negeer wijzigingen</key>
<key alias="unsavedChanges">Wijzigingen niet opgeslagen</key>
<key alias="unsavedChangesWarning">Weet je zeker dat deze pagina wilt verlaten? - er zijn onopgeslagen wijzigingen</key>
</area>
<area alias="bulk">
<key alias="done">Done</key>
<key alias="deletedItem">Deleted %0% item</key>
<key alias="deletedItems">Deleted %0% items</key>
<key alias="deletedItemOfItem">Deleted %0% out of %1% item</key>
<key alias="deletedItemOfItems">Deleted %0% out of %1% items</key>
<key alias="publishedItem">Published %0% item</key>
<key alias="publishedItems">Published %0% items</key>
<key alias="publishedItemOfItem">Published %0% out of %1% item</key>
<key alias="publishedItemOfItems">Published %0% out of %1% items</key>
<key alias="unpublishedItem">Unpublished %0% item</key>
<key alias="unpublishedItems">Unpublished %0% items</key>
<key alias="unpublishedItemOfItem">Unpublished %0% out of %1% item</key>
<key alias="unpublishedItemOfItems">Unpublished %0% out of %1% items</key>
<key alias="movedItem">Moved %0% item</key>
<key alias="movedItems">Moved %0% items</key>
<key alias="movedItemOfItem">Moved %0% out of %1% item</key>
<key alias="movedItemOfItems">Moved %0% out of %1% items</key>
<key alias="copiedItem">Copied %0% item</key>
<key alias="copiedItems">Copied %0% items</key>
<key alias="copiedItemOfItem">Copied %0% out of %1% item</key>
<key alias="copiedItemOfItems">Copied %0% out of %1% items</key>
<key alias="deletedItem">%0% item verwijderd</key>
<key alias="deletedItems">%0% items verwijderd</key>
<key alias="deletedItemOfItem">Item %0% van de %1% verwijderd</key>
<key alias="deletedItemOfItems">Items %0% van de %1% verwijderd</key>
<key alias="publishedItem">%0% item gepubliceerd</key>
<key alias="publishedItems">%0% items gepubliceerd</key>
<key alias="publishedItemOfItem">Item %0% van de %1% gepubliceerd</key>
<key alias="publishedItemOfItems">Items %0% van de %1% gepubliceerd</key>
<key alias="unpublishedItem">%0% item gedepubliceerd</key>
<key alias="unpublishedItems">%0% items gedepubliceerd</key>
<key alias="unpublishedItemOfItem">Item %0% van de %1% gedepubliceerd</key>
<key alias="unpublishedItemOfItems">Items %0% van de %1% gedepubliceerd</key>
<key alias="movedItem">%0% item verplaatst</key>
<key alias="movedItems">%0% items verplaatst</key>
<key alias="movedItemOfItem">item %0% van de %1% verplaatst</key>
<key alias="movedItemOfItems">items %0% van de %1% verplaatst</key>
<key alias="copiedItem">%0% item gekopieerd</key>
<key alias="copiedItems">%0% items gekopieerd</key>
<key alias="copiedItemOfItem">item %0% van de %1% gekopieerd</key>
<key alias="copiedItemOfItems">item %0% van de %1% gekopieerd</key>
</area>
<area alias="defaultdialogs">
<key alias="anchorInsert">Naam</key>
@@ -269,21 +290,55 @@
<key alias="thumbnailimageclickfororiginal">Klik op de afbeelding voor volledige grootte</key>
<key alias="treepicker">Kies een item</key>
<key alias="viewCacheItem">Toon cache item</key>
<key alias="createFolder">Maak folder aan...</key>
<key alias="relateToOriginalLabel">Relateer aan origineel</key>
<key alias="includeDescendants">Descendants meenemen</key>
<key alias="theFriendliestCommunity">De vriendelijkste community</key>
<key alias="linkToPage">Link naar pagina</key>
<key alias="openInNewWindow">Opent het gelinkte document in een nieuw venster of tab</key>
<key alias="linkToMedia">Link naar media</key>
<key alias="selectMedia">Selecteer media</key>
<key alias="selectIcon">Selecteer icoon</key>
<key alias="selectItem">Selecteer item</key>
<key alias="selectLink">Selecteer link</key>
<key alias="selectMacro">Selecteer macro</key>
<key alias="selectContent">Selecteer content</key>
<key alias="selectMember">Selecteer member</key>
<key alias="selectMemberGroup">Selecteer member group</key>
<key alias="noMacroParams">Er zijn geen parameters voor deze macro</key>
<key alias="externalLoginProviders">Externe login providers</key>
<key alias="exceptionDetail">Error details</key>
<key alias="stacktrace">Stacktrace</key>
<key alias="innerException">Inner Exception</key>
<key alias="linkYour">Link je</key>
<key alias="unLinkYour">De-Link je</key>
<key alias="account">account</key>
<key alias="selectEditor">Selecteer editor</key>
</area>
<area alias="dictionaryItem">
<key alias="description"><![CDATA[
Wijzig de verschillende taalversies voor het woordenboek item '%0%'. Je kunt extra talen toevoegen bij 'talen' in het menu links
]]></key>
<key alias="displayName">Cultuurnaam</key>
<key alias="changeKey">Verander de key van het dictionary item.</key>
<key alias="changeKeyError">
<![CDATA[
De key '%0%' bestaat al.
]]>
</key>
</area>
<area alias="placeholders">
<key alias="username">Typ je gebruikersnaam</key>
<key alias="password">Typ je wachtwoord</key>
<key alias="username">Typ jouw gebruikersnaam</key>
<key alias="password">Typ jouw wachtwoord</key>
<key alias="confirmPassword">Bevestig jouw wachtwoord</key>
<key alias="nameentity">Benoem de %0%...</key>
<key alias="entername">Typ een naam...</key>
<key alias="label">Label...</key>
<key alias="enterDescription">Voer een omschrijving in...</key>
<key alias="search">Typ om te zoeken...</key>
<key alias="filter">Typ om te filteren...</key>
<key alias="enterTags">Typ om tags toe te voegen (druk op enter na elke tag)...</key>
<key alias="email">Voer jouw email in</key>
</area>
<area alias="editcontenttype">
@@ -331,10 +386,17 @@
<key alias="errorRegExpWithoutTab">%0% is niet in het correcte formaat</key>
</area>
<area alias="errors">
<key alias="receivedErrorFromServer">Een error ontvangen van de server</key>
<key alias="dissallowedMediaType">Het opgegeven bestandstype is niet toegestaan door de beheerder</key>
<key alias="codemirroriewarning">OPMERKING! Ondanks dat CodeMiror is ingeschakeld, is het uitgeschakeld in Internet Explorer omdat het niet stabiel genoeg is.</key>
<key alias="contentTypeAliasAndNameNotNull">Zowel de alias als de naam van het nieuwe eigenschappen type moeten worden ingevuld!</key>
<key alias="filePermissionsError">Er is een probleem met de lees/schrijf rechten op een bestand of map</key>
<key alias="macroErrorLoadingPartialView">Error bij het laden van Partial View script (file: %0%)</key>
<key alias="macroErrorLoadingUsercontrol">Error bij het laden van userControl '%0%'</key>
<key alias="macroErrorLoadingCustomControl">Error bij het laden van customControl (Assembly: %0%, Type: '%1%')</key>
<key alias="macroErrorLoadingMacroEngineScript">Error bij het laden van MacroEngine script (file: %0%)</key>
<key alias="macroErrorParsingXSLTFile">"Error bij het parsen van XSLT file: %0%</key>
<key alias="macroErrorReadingXSLTFile">"Error bij het laden van XSLT file: %0%</key>
<key alias="missingTitle">Vul een titel in</key>
<key alias="missingType">Selecteer een type</key>
<key alias="pictureResizeBiggerThanOrg">U wilt een afbeelding groter maken dan de originele afmetingen. Weet je zeker dat je wilt doorgaan?</key>
@@ -355,102 +417,142 @@
<key alias="actions">Acties</key>
<key alias="add">Toevoegen</key>
<key alias="alias">Alias</key>
<key alias="all">Alles</key>
<key alias="areyousure">Weet je het zeker?</key>
<key alias="border">Rand</key>
<key alias="by">of</key>
<key alias="cancel">Annuleren</key>
<key alias="back">Terug</key>
<key alias="border">Border</key>
<key alias="by">bij</key>
<key alias="cancel">Cancel</key>
<key alias="cellMargin">Cel marge</key>
<key alias="choose">Kiezen</key>
<key alias="close">Sluiten</key>
<key alias="closewindow">Venster sluiten</key>
<key alias="comment">Opmerking</key>
<key alias="confirm">Bevestigen</key>
<key alias="constrainProportions">Verhouding behouden</key>
<key alias="continue">Doorgaan</key>
<key alias="copy">Kopiëren</key>
<key alias="choose">Kies</key>
<key alias="close">Sluit</key>
<key alias="closewindow">Sluit venster</key>
<key alias="comment">Comment</key>
<key alias="confirm">Bevestig</key>
<key alias="constrainProportions">Verhoudingen behouden</key>
<key alias="continue">Ga verder</key>
<key alias="copy">Copy</key>
<key alias="create">Aanmaken</key>
<key alias="database">Databank</key>
<key alias="database">Database</key>
<key alias="date">Datum</key>
<key alias="default">Standaard</key>
<key alias="delete">Verwijderen</key>
<key alias="delete">Verwijder</key>
<key alias="deleted">Verwijderd</key>
<key alias="deleting">Verwijderen...</key>
<key alias="deleting">Aan het verwijderen...</key>
<key alias="design">Ontwerp</key>
<key alias="dimensions">Afmetingen</key>
<key alias="down">Beneden</key>
<key alias="down">Omlaag</key>
<key alias="download">Download</key>
<key alias="edit">Aanpassen</key>
<key alias="edited">Aangepast</key>
<key alias="edit">Bewerk</key>
<key alias="edited">Bewerkt</key>
<key alias="elements">Elementen</key>
<key alias="email">E-mail</key>
<key alias="email">Email</key>
<key alias="error">Fout</key>
<key alias="findDocument">Zoeken</key>
<key alias="height">Hoogte</key>
<key alias="findDocument">Vind</key>
<key alias="height">Hogte</key>
<key alias="help">Help</key>
<key alias="icon">Icoon</key>
<key alias="import">Importeren</key>
<key alias="innerMargin">Binnenmarge</key>
<key alias="import">Import</key>
<key alias="innerMargin">Binnenste marge</key>
<key alias="insert">Invoegen</key>
<key alias="install">Installeren</key>
<key alias="justify">Uitvullen</key>
<key alias="invalid">Ongeldig</key>
<key alias="justify">Justify</key>
<key alias="label">Label</key>
<key alias="language">Taal</key>
<key alias="layout">Lay-out</key>
<key alias="loading">Bezig met laden</key>
<key alias="locked">Geblokkeerd</key>
<key alias="layout">Layout</key>
<key alias="loading">Aan het laden</key>
<key alias="locked">Gesloten</key>
<key alias="login">Inloggen</key>
<key alias="logoff">Afmelden</key>
<key alias="logout">Afmelden</key>
<key alias="logoff">Uitloggen</key>
<key alias="logout">Uitloggen</key>
<key alias="macro">Macro</key>
<key alias="move">Verplaatsen</key>
<key alias="more">Meer</key>
<key alias="mandatory">Verplicht</key>
<key alias="move">Verplaats</key>
<key alias="more">meer</key>
<key alias="name">Naam</key>
<key alias="new">Nieuw</key>
<key alias="next">Volgende</key>
<key alias="no">Nee</key>
<key alias="of">van</key>
<key alias="ok">Ok</key>
<key alias="open">Openen</key>
<key alias="of">of</key>
<key alias="ok">OK</key>
<key alias="open">Open</key>
<key alias="or">of</key>
<key alias="password">Wachtwoord</key>
<key alias="path">Pad</key>
<key alias="placeHolderID">Placeholder ID</key>
<key alias="pleasewait">Een ogenblik geduld a.u.b.</key>
<key alias="pleasewait">Een ogenblik geduld aub...</key>
<key alias="previous">Vorige</key>
<key alias="properties">Eigenschappen</key>
<key alias="reciept">E-mail om formulier te ontvangen</key>
<key alias="reciept">Email om formulier resultaten te ontvangen</key>
<key alias="recycleBin">Prullenbak</key>
<key alias="remaining">Overgebleven</key>
<key alias="rename">Hernoemen</key>
<key alias="recycleBinEmpty">De prullenbak is leeg</key>
<key alias="remaining">Overblijvend</key>
<key alias="rename">Hernoem</key>
<key alias="renew">Vernieuw</key>
<key alias="required" version="7.0">Verplicht</key>
<key alias="retry">Opnieuw proberen</key>
<key alias="rights">Rechten</key>
<key alias="search">Zoek</key>
<key alias="search">Zoeken</key>
<key alias="searchNoResult">We konden helaas niet vinden wat je zocht</key>
<key alias="server">Server</key>
<key alias="show">Tonen</key>
<key alias="showPageOnSend">Toon pagina bij versturen</key>
<key alias="size">Formaat</key>
<key alias="sort">Sorteren</key>
<key alias="submit">Submit</key> <!-- TODO: Translate this -->
<key alias="type">Type</key>
<key alias="typeToSearch">Type om te zoeken...</key>
<key alias="show">Toon</key>
<key alias="showPageOnSend">Toon pagina na verzenden</key>
<key alias="size">Grootte</key>
<key alias="sort">Sorteer</key>
<key alias="submit">Verstuur</key>
<key alias="type">Typen</key>
<key alias="typeToSearch">Typ om te zoeken...</key>
<key alias="up">Omhoog</key>
<key alias="update">Bijwerken</key>
<key alias="update">Update</key>
<key alias="upgrade">Upgrade</key>
<key alias="upload">Upload</key>
<key alias="url">Url</key>
<key alias="user">Gebruiker</key>
<key alias="username">Gebruikersnaam</key>
<key alias="value">Waarde</key>
<key alias="view">Toon</key>
<key alias="view">Bekijk</key>
<key alias="welcome">Welkom...</key>
<key alias="width">Breedte</key>
<key alias="yes">Ja</key>
<key alias="folder">Map</key>
<key alias="reorder">Reorder</key>
<key alias="reorderDone">I am done reordering</key>
<key alias="searchResults">Zoekresultaten</key>
<key alias="reorder">Herschik</key>
<key alias="reorderDone">Ik ben klaar met herschikken</key>
<key alias="preview">Voorvertoning</key>
<key alias="changePassword">Wachtwoord veranderen</key>
<key alias="to">naar</key>
<key alias="listView">Lijstweergave</key>
<key alias="saving">Aan het opslaan...</key>
<key alias="current">huidig</key>
<key alias="embed">Embed</key>
<key alias="selected">geselecteerd</key>
</area>
<area alias="colors">
<key alias="black">Zwart</key>
<key alias="green">Groen</key>
<key alias="yellow">Geel</key>
<key alias="orange">Oranje</key>
<key alias="blue">Blauw</key>
<key alias="red">Rood</key>
</area>
<area alias="shortcuts">
<key alias="addTab">Tab toevoegen</key>
<key alias="addProperty">Property toevoegen</key>
<key alias="addEditor">Editor toevoegen</key>
<key alias="addTemplate">Template toevoegen</key>
<key alias="addChildNode">Child node toevoegen</key>
<key alias="addChild">Child toevoegen</key>
<key alias="editDataType">Data type bewerken</key>
<key alias="navigateSections">Secties navigeren</key>
<key alias="shortcut">Shortcuts</key>
<key alias="showShortcuts">Toon shortcuts</key>
<key alias="toggleListView">Toggle lijstweergave</key>
<key alias="toggleAllowAsRoot">Toggle toestaan op root-niveau</key>
</area>
<area alias="graphicheadline">
<key alias="backgroundcolor">Achtergrondkleur</key>
@@ -528,6 +630,57 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
<key alias="watch">Bekijken</key>
<key alias="welcomeIntro"><![CDATA[Deze wizard helpt u met het configureren van <strong>Umbraco %0%</strong> voor een nieuwe installatie of een upgrade van versie 3.0. <br /><br /> Druk op <strong>"volgende"</strong> om de wizard te starten.]]></key>
</area>
<area alias="language">
<key alias="cultureCode">Cultuurcode</key>
<key alias="displayName">Cultuurnaam</key>
@@ -543,12 +696,22 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
<key alias="greeting3">Fijne woensdag</key>
<key alias="greeting4">Fijne donderdag</key>
<key alias="greeting5">Fijne vrijdag</key>
<key alias="greeting6">Fijne zaterdag</key>
<key alias="greeting6">Fijne zaterdag</key>
<key alias="instruction">log hieronder in</key>
<key alias="signInWith">Inloggen met</key>
<key alias="timeout">Sessie is verlopen</key>
<key alias="bottomText"><![CDATA[<p style="text-align:right;">&copy; 2001 - %0% <br /><a href="http://umbraco.com" style="text-decoration: none" target="_blank">umbraco.com</a></p>]]></key>
<key alias="forgottenPassword">Wachtwoord vergeten?</key>
<key alias="forgottenPasswordInstruction">Er zal een email worden gestuurd naar het emailadres van jouw account. Hierin staat een link om je wachtwoord te resetten</key>
<key alias="requestPasswordResetConfirmation">Een email met daarin de wachtwoord reset uitleg zal worden gestuurd als het emailadres in onze database voorkomt.</key>
<key alias="returnToLogin">Terug naar loginformulier</key>
<key alias="setPasswordInstruction">Geeft alsjeblieft een nieuw wachtwoord op</key>
<key alias="setPasswordConfirmation">Je wachtwoord is aangepast</key>
<key alias="resetCodeExpired">De link die je hebt aangeklikt is niet (meer) geldig.</key>
<key alias="resetPasswordEmailCopySubject">Umbraco: Wachtwoord Reset</key>
<key alias="resetPasswordEmailCopyFormat">
<![CDATA[<p>De gebruikersnaam om in te loggen bij jouw Umbraco omgeving is: <strong>%0%</strong></p><p>Klik <a href="%1%"><strong>hier</strong></a> om je wachtwoord te resetten of knip/plak deze URL in je browser:</p><p><em>%1%</em></p>]]>
</key>
</area>
<area alias="main">
<key alias="dashboard">Dashboard</key>
@@ -646,6 +809,15 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
<key alias="packageVersion">Package versie</key>
<key alias="packageVersionHistory">Package versiehistorie</key>
<key alias="viewPackageWebsite">Bekijk de package website</key>
<key alias="packageAlreadyInstalled">Package reeds geinstalleerd</key>
<key alias="targetVersionMismatch">Deze package kan niet worden geinstalleerd omdat minimaal Umbraco versie %0% benodigd is.</key>
<key alias="installStateUninstalling">Aan het deinstalleren...</key>
<key alias="installStateDownloading">Aan het downloaden...</key>
<key alias="installStateImporting">Aan het importeren...</key>
<key alias="installStateInstalling">Aan het installeren...</key>
<key alias="installStateRestarting">Aan het herstarten, een ongenblik geduld aub...</key>
<key alias="installStateComplete">Geinstalleerd! Je browser zal nu automatisch ververst worden...</key>
<key alias="installStateCompleted">Kruk op "finish" om de installate te voltooien en de pagina te verversen.</key>
</area>
<area alias="paste">
<key alias="doNothing">Plakken met alle opmaak (Niet aanbevolen)</key>
@@ -677,13 +849,18 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
%0% kan niet worden gepubliceerd omdat het item is gepland voor release.
]]>
</key>
<key alias="contentPublishedFailedExpired"><![CDATA[
%0% kon niet gepubliceerd worden omdat het item niet meer geldig is.
]]></key>
<key alias="contentPublishedFailedInvalid"><![CDATA[
%0% kan niet worden gepubliceerd, omdat de eigenschappen:%1% de validatieregels niet hebben doorstaan.
]]></key>
<key alias="contentPublishedFailedByEvent"><![CDATA[
%0% kon niet worden gepubliceerd doordat een 3rd party extensie het heeft geannuleerd.
%0% kon niet worden gepubliceerd doordat een 3rd party extensie het heeft geannuleerd.
]]></key>
<key alias="contentPublishedFailedByParent"><![CDATA[
%0% kon niet gepubliceerd worden, omdat de parent pagina niet gepubliceerd is.
]]></key>
<key alias="includeUnpublished">Inclusief ongepubliceerde kinderen</key>
<key alias="inProgress">Publicatie in uitvoering - even geduld...</key>
<key alias="inProgressCounter">%0% van %1% paginas zijn gepubliceerd...</key>
@@ -698,16 +875,13 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
<key alias="noColors">Je hebt geen goedgekeurde kleuren geconfigureerd</key>
</area>
<area alias="relatedlinks">
<key alias="addExternal">Externe link toevoegen</key>
<key alias="addInternal">Interne link toevoegen</key>
<key alias="addlink">Toevoegen</key>
<key alias="caption">Bijschrift</key>
<key alias="internalPage">Interne pagina</key>
<key alias="linkurl">URL</key>
<key alias="modeDown">Verplaats omlaag</key>
<key alias="modeUp">Verplaats omhoog</key>
<key alias="newWindow">Open in nieuw venster</key>
<key alias="removeLink">Verwijder link</key>
<key alias="enterExternal">Externe link toevoegen</key>
<key alias="chooseInternal">Interne link toevoegen</key>
<key alias="caption">Bijschrift</key>
<key alias="link">Link</key>
<key alias="newWindow">In een nieuw venster openen</key>
<key alias="captionPlaceholder">Voer het bijschrijft in</key>
<key alias="externalLinkPlaceholder">Voer de link in</key>
</area>
<area alias="imagecropper">
<key alias="reset">Reset</key>
@@ -736,13 +910,17 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
<key alias="settings">Instellingen</key>
<key alias="statistics">Statistieken</key>
<key alias="translation">Vertaling</key>
<key alias="users">Gebruikers</key>
<key alias="contour" version="4.0">Umbraco Contour</key>
<key alias="users">Gebruikers</key>
<key alias="help" version="7.0">Help</key>
<key alias="forms">Formulieren</key>
<key alias="analytics">Analytics</key>
</area>
<area alias="help">
<key alias="goTo">ga naar</key>
<key alias="helpTopicsFor">Help onderwerpen voor</key>
<key alias="videoChaptersFor">Video's voor</key>
<key alias="theBestUmbracoVideoTutorials">De beste Umbraco video tutorials</key>
</area>
<area alias="settings">
<key alias="defaulttemplate">Standaard template</key>
<key alias="dictionary editor egenskab">Woordenboek sleutel</key>
@@ -762,6 +940,7 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
<key alias="noPropertiesDefinedOnTab">Geen eigenschappen gedefinieerd op dit tabblad. Klik op de link "voeg een nieuwe eigenschap" aan de bovenkant om een nieuwe eigenschap te creëren.</key>
<key alias="masterDocumentType">Master Document Type</key>
<key alias="createMatchingTemplate">Maak een bijbehorend template</key>
<key alias="addIcon">Icon toevoegen</key>
</area>
<area alias="sort">
<key alias="sortOrder">Sort order</key>
@@ -771,7 +950,13 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
<key alias="sortPleaseWait"><![CDATA[Een ogenblik geduld. Paginas worden gesorteerd, dit kan even duren.<br/> <br/> Sluit dit venster niet tijdens het sorteren]]></key>
</area>
<area alias="speechBubbles">
<key alias="contentPublishedFailedByEvent">Publicatie werd geannuleerd door een 3rd party plug-in</key>
<key alias="validationFailedHeader">Validatie</key>
<key alias="validationFailedMessage">Validatiefouten moeten worden opgelost voor dit item kan worden opgeslagen</key>
<key alias="operationFailedHeader">Mislukt</key>
<key alias="invalidUserPermissionsText">Wegens onvoldoende rechten kon deze handeling kon niet worden uitegevoerd </key>
<key alias="operationCancelledHeader">Geannuleerd</key>
<key alias="operationCancelledText">Uitvoering is g eannuleerd door de plugin van een 3e partij</key>
<key alias="contentPublishedFailedByEvent">Publicatie werd geannuleerd doordeeen plugin van een 3e partij</key>
<key alias="contentTypeDublicatePropertyType">Eigenschappen type bestaat al</key>
<key alias="contentTypePropertyTypeCreated">Eigenschappen type aangemaakt</key>
<key alias="contentTypePropertyTypeCreatedText"><![CDATA[Naam: %0% <br /> Data type: %1%]]></key>
@@ -806,6 +991,8 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
<key alias="fileSavedHeader">Bestand opgeslagen</key>
<key alias="fileSavedText">Bestand opgeslagen zonder fouten</key>
<key alias="languageSaved">Taal opgeslagen</key>
<key alias="mediaTypeSavedHeader">Media Type opgeslagen</key>
<key alias="memberTypeSavedHeader">Member Type opgeslagen</key>
<key alias="pythonErrorHeader">Python script niet opgeslagen</key>
<key alias="pythonErrorText">Python script kon niet worden opgeslagen door een fout</key>
<key alias="pythonSavedHeader">Python script opeslagen!</key>
@@ -824,6 +1011,11 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
<key alias="partialViewSavedText">Partial view opgeslagen zonder fouten!</key>
<key alias="partialViewErrorHeader">Partial view niet opgeslagen</key>
<key alias="partialViewErrorText">Er is een fout opgetreden bij het opslaan van het bestand.</key>
<key alias="scriptSavedHeader">Script view opgeslagen</key>
<key alias="scriptSavedText">Script view opgeslagen zonder fouten!</key>
<key alias="scriptErrorHeader">Script view niet opgeslagen</key>
<key alias="scriptErrorText">Er is een fout opgetreden bij het opslaan van dit bestand.</key>
<key alias="cssErrorText">Er is een fout opgetreden bij het opslaan van dit bestand.</key>
</area>
<area alias="stylesheet">
<key alias="aliasHelp">Gebruik CSS syntax bijv: h1, .redHeader, .blueTex</key>
@@ -846,15 +1038,15 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
</area>
<area alias="grid">
<key alias="insertControl">Item toevoegen</key>
<key alias="chooseLayout">Choose a layout</key>
<key alias="addRows">Een rij aan de lay-out toevoegen</key>
<key alias="addElement"><![CDATA[Klik om te starten op het <i class="icon icon-add blue"></i> teken onderaan en voeg je eerste item toe]]></key>
<key alias="chooseLayout">Kies de indeling</key>
<key alias="addRows">Kies een indeling voor deze pagina om content toe te kunnen voegen</key>
<key alias="addElement"><![CDATA[<i class="icon icon-add blue"></i> Plaats een (extra) content blok]]></key>
<key alias="dropElement">Drop content</key>
<key alias="settingsApplied">Settings applied</key>
<key alias="contentNotAllowed">This content is not allowed here</key>
<key alias="contentAllowed">This content is allowed here</key>
<key alias="settingsApplied">Instellingen toegepast</key>
<key alias="contentNotAllowed">Deze content is hier niet toegestaan</key>
<key alias="contentAllowed">Deze content is hier toegestaan</key>
<key alias="clickToEmbed">Klik om een item te embedden</key>
<key alias="clickToInsertImage">Klik om een afbeelding in te voegen</key>
<key alias="placeholderImageCaption">Afbeelding ondertitel...</key>
@@ -883,7 +1075,79 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
<key alias="allowAllEditors">Alle editors toelaten</key>
<key alias="allowAllRowConfigurations">Alle rijconfiguraties toelaten</key>
<key alias="setAsDefault">Instellen als standaard</key>
<key alias="chooseExtra">Kies extra</key>
<key alias="chooseDefault">Kies standaard</key>
<key alias="areAdded">zijn toegevoegd</key>
</area>
<area alias="contentTypeEditor">
<key alias="compositions">Composities</key>
<key alias="noTabs">Er zijn nog geen tabs toegevoegd</key>
<key alias="addNewTab">Voeg een nieuwe tab toe</key>
<key alias="addAnotherTab">Voeg nog een tab toe</key>
<key alias="inheritedFrom">Inherited van</key>
<key alias="addProperty">Voeg property toe</key>
<key alias="requiredLabel">Verplicht label</key>
<key alias="enableListViewHeading">Zet list view aan</key>
<key alias="enableListViewDescription">Laat de child nodes van het content item zien als een sorteer- en doorzoekbare lijstweergave zien. Deze child nodes worden dan niet in de boomstructuur getoond.</key>
<key alias="allowedTemplatesHeading">Toegestane Templates</key>
<key alias="allowedTemplatesDescription">Kies welke templates toegestaan zijn om door de editors op dit content-type gebruikt te worden</key>
<key alias="allowAsRootHeading">Sta toe op root-niveau</key>
<key alias="allowAsRootDescription">Sta editors toe om content van dit type aan te maken op root-niveau</key>
<key alias="allowAsRootCheckbox">Ja - sta content van dit type toe op root-niveau</key>
<key alias="childNodesHeading">Toegestane child node types</key>
<key alias="childNodesDescription">Sta contetn van een bepaalde type toe om onder dit type aangemaakt te kunnen worden</key>
<key alias="chooseChildNode">Kies child node</key>
<key alias="compositionsDescription">Overerfde tabs en properties van een bestaand document-type. Nieuwe tabs worden toegevoegd aan het huidige document-type of samengevoegd als een tab met de identieke naam al bestaat.</key>
<key alias="compositionInUse">Dit content-type wordt gebruikt in een compositie en kan daarom niet zelf een compositie worden.</key>
<key alias="noAvailableCompositions">Er zijn geen content-typen beschikbaar om als compositie te gebruiken.</key>
<key alias="availableEditors">Beschikbare editors</key>
<key alias="reuse">Herbruik</key>
<key alias="editorSettings">Editor instellingen</key>
<key alias="configuration">Configuratie</key>
<key alias="yesDelete">Ja, verwijder</key>
<key alias="movedUnderneath">is naar onder geplaatst</key>
<key alias="copiedUnderneath">is naar onder gecopierd</key>
<key alias="folderToMove">Selecteer de map om te verplaatsen</key>
<key alias="folderToCopy">Selecteer de map om te kopieren</key>
<key alias="structureBelow">naar de boomstructuur onder</key>
<key alias="allDocumentTypes">Alle Document types</key>
<key alias="allDocuments">Alle documenten</key>
<key alias="allMediaItems">Alle media items</key>
<key alias="usingThisDocument">die gebruik maken van dit document type zullen permanent verwijderd worden. Bevestig aub dat je deze ook wilt verwijderen.</key>
<key alias="usingThisMedia">die gebruik maken van dit media type zullen permanent verwijderd worden. Bevestig aub dat je deze ook wilt verwijderen.</key>
<key alias="usingThisMember">die gebruik maken van dit member type zullen permanent verwijderd worden. Bevestig aub dat je deze ook wilt verwijderen.</key>
<key alias="andAllDocuments">en alle documenten van dit type</key>
<key alias="andAllMediaItems">en alle media items van dit type</key>
<key alias="andAllMembers">en alle leden van dit type</key>
<key alias="thisEditorUpdateSettings">die gebruik maken van deze editor zullen geupdate worden met deze nieuwe instellingen</key>
<key alias="memberCanEdit">Lid kan bewerken</key>
<key alias="showOnMemberProfile">Toon in het profiel van leden</key>
<key alias="tabHasNoSortOrder">tab heeft geen sorteervolgorde</key>
</area>
<area alias="modelsBuilder">
<key alias="buildingModels">Models aan het gereneren</key>
<key alias="waitingMessage">dit kan enige tijd duren, geduld aub</key>
<key alias="modelsGenerated">Models gegenereerd</key>
<key alias="modelsGeneratedError">Models konden niet gegenereerd worden</key>
<key alias="modelsExceptionInUlog">Models generatie is mislukt, kijk in de Umbraco log voor details</key>
</area>
<area alias="templateEditor">
<key alias="alternativeField">Alternatief veld</key>
<key alias="alternativeText">Alternatieve tekst</key>
@@ -916,7 +1180,8 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
</area>
<area alias="translation">
<key alias="assignedTasks">Taken aan jou toegewezen</key>
<key alias="assignedTasksHelp"><![CDATA[Onderstaande lijst toont vertalingstaken aan jou toegewezen. Om een meer gedetailleerd overzicht te zien, met comments, klik op "Details" of de naam van de pagina. Je kan ook de pagina direct downloaden als XML door te klikken op "Download Xml".
<key alias="assignedTasksHelp"><![CDATA[Onderstaande lijst toont vertalingstaken aan jou toegewezen. Om een meer gedetailleerd overzicht te zien, met comments, klik op "Details" of de naam van de pagina.
Je kan ook de pagina direct downloaden als XML door te klikken op "Download Xml".
Om een vertalingstaak te sluiten, ga aub naar het detailoverzicht en klik op de "Sluit" knop.
]]></key>
<key alias="closeTask">Sluit taak</key>
@@ -932,18 +1197,24 @@ Om een vertalingstaak te sluiten, ga aub naar het detailoverzicht en klik op de
Dit is een geautomatiseerde mail om u op de hoogte te brengen dat document '%1%'
is aangevraagd voor vertaling naar '%5%' door %2%.
Ga naar http://%3%/Umbraco/translation/default.aspx?id=%4% om te bewerken.
Ga naar http://%3%/translation/details.aspx?id=%4% om te bewerken.
Of log in bij Umbraco om een overzicht te krijgen van al jouw vertalingen.
Een prettige dag!
Met vriendelijke groet!
Dit is een bericht van uw Content Management Systeem.
De Umbraco Robot
]]></key>
<key alias="mailSubject">[%0%] Vertaalopdracht voor %1%</key>
<key alias="noTranslators">Geen vertaal-gebruikers gevonden. Maak eerst een vertaal-gebruiker aan voordat je pagina's voor vertaling verstuurd</key>
<key alias="ownedTasks">Taken aangemaakt door jou</key>
<key alias="ownedTasksHelp"><![CDATA[De lijst hieronder toont pagina's <strong>die je aanmaakte</strong>. Om een detailweergave met opmerkingen te zien, klik op "Detail" of op de paginanaam. Je kan ook de pagina in XML-formaat downloaden door op de "Download XML"-link te klikken. Om een vertalingstaak te sluiten, klik je op de "Sluiten"-knop in detailweergave.]]></key>
<key alias="ownedTasksHelp"><![CDATA[De lijst hieronder toont pagina's <strong>die je aanmaakte</strong>.
Om een detailweergave met opmerkingen te zien, klik op "Detail" of op de paginanaam.
Je kan ook de pagina in XML-formaat downloaden door op de "Download XML"-link te klikken.
Om een vertalingstaak te sluiten, klik je op de "Sluiten"-knop in detailweergave.]]></key>
<key alias="pageHasBeenSendToTranslation">De pagina '%0%' is verstuurd voor vertaling</key>
<key alias="noLanguageSelected">Kies de taal waarin deze contetn vertaald moet worden</key>
<key alias="sendToTranslate">Stuur voor vertaling</key>
<key alias="taskAssignedBy">Toegewezen door</key>
<key alias="taskOpened">Taak geopend</key>
@@ -973,7 +1244,8 @@ Om een vertalingstaak te sluiten, ga aub naar het detailoverzicht en klik op de
<key alias="memberGroups">Ledengroepen</key>
<key alias="memberRoles">Rollen</key>
<key alias="memberTypes">Ledentypes</key>
<key alias="documentTypes">Documenttypes</key>
<key alias="documentTypes">Documenttypen</key>
<key alias="relationTypes">RelatieTypen</key>
<key alias="packager">Packages</key>
<key alias="packages">Packages</key>
<key alias="python">Python-bestanden</key>
@@ -985,6 +1257,7 @@ Om een vertalingstaak te sluiten, ga aub naar het detailoverzicht en klik op de
<key alias="stylesheets">Stylesheets</key>
<key alias="templates">Sjablonen</key>
<key alias="xslt">XSLT Bestanden</key>
<key alias="analytics">Analytics</key>
</area>
<area alias="update">
<key alias="updateAvailable">Nieuwe update beschikbaar</key>
@@ -1010,6 +1283,7 @@ Om een vertalingstaak te sluiten, ga aub naar het detailoverzicht en klik op de
<key alias="mediastartnode">Startnode in Mediabibliotheek</key>
<key alias="modules">Secties</key>
<key alias="noConsole">Blokkeer Umbraco toegang</key>
<key alias="oldPassword">Oude wachtwoord</key>
<key alias="password">Wachtwoord</key>
<key alias="resetPassword">Reset wachtwoord</key>
<key alias="passwordChanged">Je wachtwoord is veranderd!</key>
@@ -1030,10 +1304,138 @@ Om een vertalingstaak te sluiten, ga aub naar het detailoverzicht en klik op de
<key alias="usertype">Gebruikerstype</key>
<key alias="userTypes">Gebruikerstypes</key>
<key alias="writer">Auteur</key>
<key alias="translator">Vertaler</key>
<key alias="change">Wijzig</key>
<key alias="yourProfile" version="7.0">Je profiel</key>
<key alias="yourHistory" version="7.0">Je recente historie</key>
<key alias="sessionExpires" version="7.0">Sessie verloopt over</key>
</area>
<area alias="validation">
<key alias="validation">Validatie</key>
<key alias="validateAsEmail">Valideer als email</key>
<key alias="validateAsNumber">Valideer als nummer</key>
<key alias="validateAsUrl">Valideer als Url</key>
<key alias="enterCustomValidation">...of gebruik custom validatie</key>
<key alias="fieldIsMandatory">Veld is verplicht</key>
</area>
<area alias="healthcheck">
<!-- The following keys get these tokens passed in:
0: Current value
1: Recommended value
2: XPath
3: Configuration file path
-->
<key alias="checkSuccessMessage">Waarde is insteld naar the aanbevolen waarde: '%0%'.</key>
<key alias="rectifySuccessMessage">Waarde was '%1%' voor XPath '%2%' in configuratie bestand '%3%'.</key>
<key alias="checkErrorMessageDifferentExpectedValue">De verwachtte waarde voor '%2%' is '%1%' in configuratie bestand '%3%', maar is '%0%'.</key>
<key alias="checkErrorMessageUnexpectedValue">Onverwachte waarde '%0%' gevonden voor '%2%' in configuratie bestand '%3%'.</key>
<!-- The following keys get these tokens passed in:
0: Current value
1: Recommended value
-->
<key alias="customErrorsCheckSuccessMessage">Custom foutmeldingen zijn ingesteld op '%0%'.</key>
<key alias="customErrorsCheckErrorMessage">Custom foutmeldingen zijn momenteel '%0%'. Wij raden aan deze aan te passen naar '%1%' voor livegang.</key>
<key alias="customErrorsCheckRectifySuccessMessage">Custom foutmeldingen aangepast naar '%0%'.</key>
<key alias="macroErrorModeCheckSuccessMessage">Macro foutmeldingen zijn ingesteld op'%0%'.</key>
<key alias="macroErrorModeCheckErrorMessage">Macro foutmeldingen zijn ingesteld op '%0%'. Dit zal er voor zorgen dat bepaalde, of alle, pagina's van de website niet geladen kunnen worden als er errors in een Macro zitten. Corrigeren zal deze waarde aanpassen naar '%1%'.</key>
<key alias="macroErrorModeCheckRectifySuccessMessage">Macro foutmeldingen zijn aangepast naar '%0%'.</key>
<!-- The following keys get these tokens passed in:
0: Current value
1: Recommended value
2: Server version
-->
<key alias="trySkipIisCustomErrorsCheckSuccessMessage">Try Skip IIS Custom foutmeldingen is ingesteld op '%0%'. IIS versie '%1%' wordt gebruikt.</key>
<key alias="trySkipIisCustomErrorsCheckErrorMessage">Try Skip IIS Custom foutmeldingen is ingesteld op '%0%'. Het wordt voor de gebruikte IIS versie (%2%) aangeraden deze in te stellen op '%1%'.</key>
<key alias="trySkipIisCustomErrorsCheckRectifySuccessMessage">Try Skip IIS Custom foutmeldingen ingesteld op '%0%'.</key>
<!-- The following keys get predefined tokens passed in that are not all the same, like above -->
<key alias="configurationServiceFileNotFound">Het volgende bestand bestaat niet: '%0%'.</key>
<key alias="configurationServiceNodeNotFound"><![CDATA[<strong>'%0%'</strong> kon niet gevonden worden in configuratie bestand <strong>'%1%'</strong>.]]></key>
<key alias="configurationServiceError">Er is een fout opgetreden. Bekijk de log file voor de volledige fout: %0%.</key>
<key alias="xmlDataIntegrityCheckMembers">Members - Totaal XML: %0%, Totaal: %1%, Total incorrect: %2%</key>
<key alias="xmlDataIntegrityCheckMedia">Media - Totaal XML: %0%, Totaal: %1%, Total incorrect: %2%</key>
<key alias="xmlDataIntegrityCheckContent">Content - Totaal XML: %0%, Totaal gepubliceerd: %1%, Total incorrect: %2%</key>
<key alias="httpsCheckValidCertificate">Het cerficaat van de website is ongeldig.</key>
<key alias="httpsCheckInvalidCertificate">Cerficaat validatie foutmelding: '%0%'</key>
<key alias="httpsCheckInvalidUrl">Fout bij pingen van URL %0% - '%1%'</key>
<key alias="httpsCheckIsCurrentSchemeHttps">De site wordt momenteel %0% bekeken via HTTPS.</key>
<key alias="httpsCheckConfigurationRectifyNotPossible">De appSetting 'umbracoUseSSL' in web.config staat op 'false'. Indien HTTPS gebruikt wordt moet deze op 'true' staan.</key>
<key alias="httpsCheckConfigurationCheckResult">De appSetting 'umbracoUseSSL' in web.config is ingesteld op '%0%'. Cookies zijn %1% ingesteld als secure.</key>
<key alias="httpsCheckEnableHttpsError">De 'umbracoUseSSL' waarde in web.config kon niet aangepast worden. Foutmelding: %0%</key>
<!-- The following keys don't get tokens passed in -->
<key alias="httpsCheckEnableHttpsButton">HTTPS inschakelen</key>
<key alias="httpsCheckEnableHttpsDescription">Zet in de appSettings van de web.config de umbracoSSL instelling op 'true'.</key>
<key alias="httpsCheckEnableHttpsSuccess">De appSetting 'umbracoUseSSL' is nu ingesteld op 'true', cookies zullen als 'secure' worden aangemerkt.</key>
<key alias="rectifyButton">Fix</key>
<key alias="cannotRectifyShouldNotEqual">Cannot fix a check with a value comparison type of 'ShouldNotEqual'.</key>
<key alias="cannotRectifyShouldEqualWithValue">Cannot fix a check with a value comparison type of 'ShouldEqual' with a provided value.</key>
<key alias="valueToRectifyNotProvided">Value to fix check not provided.</key>
<key alias="compilationDebugCheckSuccessMessage">Debug compiliate mode staat uit.</key>
<key alias="compilationDebugCheckErrorMessage">Debug compiliate mode staat momenteel aan. Wij raden aan deze instelling uit te zetten voor livegang.</key>
<key alias="compilationDebugCheckRectifySuccessMessage">Debug compiliate mode uitgezet.</key>
<key alias="traceModeCheckSuccessMessage">Trace mode staat uit.</key>
<key alias="traceModeCheckErrorMessage">Trace mode staat momenteel aan. Wij raden aan deze instelling uit te zetten voor livegang.</key>
<key alias="traceModeCheckRectifySuccessMessage">Trace mode uitgezet.</key>
<key alias="folderPermissionsCheckMessage">Alle mappen hebben de juiste permissie-instellingen!.</key>
<!-- The following keys get these tokens passed in:
0: Comma delimitted list of failed folder paths
-->
<key alias="requiredFolderPermissionFailed"><![CDATA[De volgende bestanden moeten wijzig-rechten krijgen om Umbraco goed te laten werken: <strong>%0%</strong>.]]></key>
<key alias="optionalFolderPermissionFailed"><![CDATA[Aangeraden wordt de volgende bestanden wijzig-rechten te geven om Umbraco goed te laten werken: <strong>%0%</strong>. Als deze niet in gebruik zijn voor deze omgeving hoeft er geen actie te worden ondernomen.]]></key>
<key alias="filePermissionsCheckMessage">All files have the correct permissions set.</key>
<!-- The following keys get these tokens passed in:
0: Comma delimitted list of failed folder paths
-->
<key alias="requiredFilePermissionFailed"><![CDATA[De volgende bestanden moeten schrijf-rechten krijgen om Umbraco goed te laten werken: <strong>%0%</strong>.]]></key>
<key alias="optionalFilePermissionFailed"><![CDATA[Aangeraden wordt de volgende bestanden schrijf-rechten te geven om Umbraco goed te laten werken: <strong>%0%</strong>. Als deze niet in gebruik zijn voor deze omgeving hoeft er geen actie te worden ondernomen.]]></key>
<key alias="clickJackingCheckHeaderFound"><![CDATA[De <strong>X-Frame-Options</strong> header of meta-tag om IFRAMEing door andere websites te voorkomen is aanwezig!]]></key>
<key alias="clickJackingCheckHeaderNotFound"><![CDATA[De <strong>X-Frame-Options</strong> header of meta-tag om IFRAMEing door andere websites te voorkomen is NIET aanwezig.]]></key>
<key alias="clickJackingSetHeaderInConfig">Voorkom IFRAMEing via web.config</key>
<key alias="clickJackingSetHeaderInConfigDescription">Voegt de instelling toe aan de httpProtocol/customHeaders section in web.config om IFRAMEing door andere websites te voorkomen.</key>
<key alias="clickJackingSetHeaderInConfigSuccess">De instelling om IFRAMEing door andere websites te voorkomen is toegevoegd aan de web.config!</key>
<key alias="clickJackingSetHeaderInConfigError">Web.config kon niet aangepast worden door error: %0%</key>
<!-- The following key get these tokens passed in:
0: Comma delimitted list of headers found
-->
<key alias="excessiveHeadersFound"><![CDATA[De volgende header welke informatie tonen over de gebruikte website technologie zijn aangetroffen: <strong>%0%</strong>.]]></key>
<key alias="excessiveHeadersNotFound">Er zijn geen headeres gevonden welke informatie over de gebruikte website technologie prijsgeven!</key>
<key alias="smtpMailSettingsNotFound">In de Web.config werd system.net/mailsettings niet gevonden</key>
<key alias="smtpMailSettingsHostNotConfigured">In de Web.config sectie system.net/mailsettings is de host niet geconfigureerd.</key>
<key alias="smtpMailSettingsConnectionSuccess">SMTP instellingen zijn correct ingesteld en werken zoals verwacht.</key>
<key alias="smtpMailSettingsConnectionFail">De SMTP server geconfigureerd met host '%0%' en poort '%1%' kon niet gevonden worden. Controleer of de SMTP instellingen in Web.config file system.net/mailsettings correct zijn.</key>
<key alias="notificationEmailsCheckSuccessMessage"><![CDATA[Notificatie email is verzonden naar <strong>%0%</strong>.]]></key>
<key alias="notificationEmailsCheckErrorMessage"><![CDATA[Notificatie email staat nog steeds op de default waarde van <strong>%0%</strong>.]]></key>
</area>
<area alias="redirectUrls">
<key alias="disableUrlTracker">URL tracker uitzetten</key>
<key alias="enableUrlTracker">URL tracker aanzetten</key>
<key alias="originalUrl">Originele URL</key>
<key alias="redirectedTo">Doorgestuurd naar</key>
<key alias="noRedirects">Er zijn geen redirects</key>
<key alias="noRedirectsDescription">Er wordt automatisch een redirect aangemaakt als een gepubliceerde pagina hernoemd of verplaatst wordt.</key>
<key alias="removeButton">Verwijder</key>
<key alias="confirmRemove">Weet je zeker dat je de redirect van '%0%' naar '%1%' wilt verwijderen?</key>
<key alias="redirectRemoved">Redirect URL verwijderd.</key>
<key alias="redirectRemoveError">Fout bij verwijderen redirect URL.</key>
<key alias="confirmDisable">Weet je zeker dat je de URL tracker wilt uitzetten?</key>
<key alias="disabledConfirm">URL tracker staat nu uit.</key>
<key alias="disableError">Fout bij het uitzetten van de URL Tracker. Meer informatie kan gevonden worden in de log file.</key>
<key alias="enabledConfirm">URL tracker staat nu aan.</key>
<key alias="enableError">Fout bij het aanzetten van de URL tracker. Meer informatie kan gevonden worden in de log file.</key>
</area>
</language>
@@ -27,6 +27,9 @@
<key alias="publish">Опубликовать</key>
<key alias="refreshNode">Обновить узлы</key>
<key alias="republish">Опубликовать весь сайт</key>
<key alias="SetPermissionsForThePage">Установить разрешения для страницы '%0%'</key>
<key alias="chooseWhereToMove">Выберите, куда переместить</key>
<key alias="toInTheTreeStructureBelow">В структуре документов ниже</key>
<key alias="restore" version="7.3.0">Восстановить</key>
<key alias="rights">Разрешения</key>
<key alias="rollback">Откатить</key>
@@ -189,7 +192,7 @@
<key alias="memberrole">Роль участника</key>
<key alias="membertype">Тип участника</key>
<key alias="noDate">Дата не указана</key>
<key alias="nodeName">Заголовок страницы</key>
<key alias="nodeName">Заголовок ссылки</key>
<key alias="notmemberof">Не является членом групп(ы)</key>
<key alias="otherElements">Свойства</key>
<key alias="parentNotPublished">Этот документ опубликован, но скрыт, потому что его родительский документ '%0%' не опубликован</key>
@@ -341,9 +344,10 @@
<key alias="viewCacheItem">Просмотр элемента кэша</key>
<key alias="createFolder">Создать папку...</key>
<key alias="relateToOriginalLabel">Связать с оригиналом</key>
<key alias="includeDescendants">Включая все дочерние</key>
<key alias="theFriendliestCommunity">Самое дружелюбное сообщество</key>
<key alias="linkToPage">Ссылка на страницу</key>
<key alias="openInNewWindow">Открывает документ по ссылке в новом окне или вкладке браузера</key>
<key alias="openInNewWindow">Открывать ссылку в новом окне или вкладке браузера</key>
<key alias="linkToMedia">Ссылка на медиа-файл</key>
<key alias="selectMedia">Выбрать медиа</key>
<key alias="selectIcon">Выбрать значок</key>
@@ -131,9 +131,7 @@ namespace Umbraco.Web.Cache
ClearAllIsolatedCacheByEntityType<IMediaType>();
ClearAllIsolatedCacheByEntityType<IMember>();
ClearAllIsolatedCacheByEntityType<IMemberType>();
//all property type cache
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.PropertyTypeCacheKey);
//all content type property cache
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.ContentTypePropertiesCacheKey);
//all content type cache
@@ -266,12 +264,6 @@ namespace Umbraco.Web.Cache
/// </returns>
private static void ClearContentTypeCache(JsonPayload payload)
{
//clears the cache for each property type associated with the content type
foreach (var pid in payload.PropertyTypeIds)
{
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheItem(CacheKeys.PropertyTypeCacheKey + pid);
}
//clears the cache associated with the Content type itself
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheItem(string.Format("{0}{1}", CacheKeys.ContentTypeCacheKey, payload.Id));
//clears the cache associated with the content type properties collection
+2 -2
View File
@@ -74,7 +74,7 @@ namespace Umbraco.Web.Cache
public override void Remove(int id)
{
ApplicationContext.Current.ApplicationCache.ClearPartialViewCache();
content.Instance.ClearDocumentCache(id);
content.Instance.ClearDocumentCache(id, false);
DistributedCache.Instance.ClearAllMacroCacheOnCurrentServer();
DistributedCache.Instance.ClearXsltCacheOnCurrentServer();
ClearAllIsolatedCacheByEntityType<PublicAccessEntry>();
@@ -95,7 +95,7 @@ namespace Umbraco.Web.Cache
public override void Remove(IContent instance)
{
ApplicationContext.Current.ApplicationCache.ClearPartialViewCache();
content.Instance.ClearDocumentCache(new Document(instance));
content.Instance.ClearDocumentCache(new Document(instance), false);
XmlPublishedContent.ClearRequest();
DistributedCache.Instance.ClearAllMacroCacheOnCurrentServer();
DistributedCache.Instance.ClearXsltCacheOnCurrentServer();
+1 -1
View File
@@ -614,7 +614,7 @@ namespace Umbraco.Web.Editors
.Select(Mapper.Map<EntityBasic>);
// entities are in "some" order, put them back in order
var xref = entities.ToDictionary(x => x.Id);
var xref = entities.ToDictionary(x => x.Key);
var result = keysArray.Select(x => xref.ContainsKey(x) ? xref[x] : null).Where(x => x != null);
return result;
+9 -4
View File
@@ -4,21 +4,26 @@ using System.Net;
using System.Net.Http;
using System.Text;
using System.Web.Http;
using System.Web.SessionState;
using AutoMapper;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
using umbraco;
using Umbraco.Core;
namespace Umbraco.Web.Editors
{
/// <summary>
/// API controller to deal with Macro data
/// </summary>
/// <remarks>
/// Note that this implements IRequiresSessionState which will enable HttpContext.Session - generally speaking we don't normally
/// enable this for webapi controllers, however since this controller is used to render macro content and macros can access
/// Session, we don't want it to throw null reference exceptions.
/// </remarks>
[PluginController("UmbracoApi")]
public class MacroController : UmbracoAuthorizedJsonController
public class MacroController : UmbracoAuthorizedJsonController, IRequiresSessionState
{
/// <summary>
/// Gets the macro parameters to be filled in for a particular macro
/// </summary>
@@ -124,6 +129,6 @@ namespace Umbraco.Web.Editors
"text/html");
return result;
}
}
}
+98 -5
View File
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Net.Http;
@@ -27,6 +28,7 @@ using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi;
using System.Linq;
using System.Runtime.Serialization;
using System.Web.Http.Controllers;
using Umbraco.Web.WebApi.Binders;
using Umbraco.Web.WebApi.Filters;
using umbraco;
@@ -36,6 +38,7 @@ using Umbraco.Core.Configuration;
using Umbraco.Core.Persistence.FaultHandling;
using Umbraco.Web.UI;
using Notification = Umbraco.Web.Models.ContentEditing.Notification;
using Umbraco.Core.Persistence;
namespace Umbraco.Web.Editors
{
@@ -44,9 +47,22 @@ namespace Umbraco.Web.Editors
/// access to ALL of the methods on this controller will need access to the media application.
/// </remarks>
[PluginController("UmbracoApi")]
[UmbracoApplicationAuthorizeAttribute(Constants.Applications.Media)]
[UmbracoApplicationAuthorize(Constants.Applications.Media)]
[MediaControllerControllerConfiguration]
public class MediaController : ContentControllerBase
{
/// <summary>
/// Configures this controller with a custom action selector
/// </summary>
private class MediaControllerControllerConfigurationAttribute : Attribute, IControllerConfiguration
{
public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
{
controllerSettings.Services.Replace(typeof(IHttpActionSelector), new ParameterSwapControllerActionSelector(
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetChildren", "id", typeof(int), typeof(Guid), typeof(string))));
}
}
/// <summary>
/// Constructor
/// </summary>
@@ -172,7 +188,7 @@ namespace Umbraco.Web.Editors
}
/// <summary>
/// Returns the child media objects
/// Returns the child media objects - using the entity INT id
/// </summary>
[FilterAllowedOutgoingMedia(typeof(IEnumerable<ContentItemBasic<ContentPropertyBasic, IMedia>>), "Items")]
public PagedResult<ContentItemBasic<ContentPropertyBasic, IMedia>> GetChildren(int id,
@@ -209,6 +225,58 @@ namespace Umbraco.Web.Editors
return pagedResult;
}
/// <summary>
/// Returns the child media objects - using the entity GUID id
/// </summary>
/// <param name="id"></param>
/// <param name="pageNumber"></param>
/// <param name="pageSize"></param>
/// <param name="orderBy"></param>
/// <param name="orderDirection"></param>
/// <param name="orderBySystemField"></param>
/// <param name="filter"></param>
/// <returns></returns>
[FilterAllowedOutgoingMedia(typeof(IEnumerable<ContentItemBasic<ContentPropertyBasic, IMedia>>), "Items")]
public PagedResult<ContentItemBasic<ContentPropertyBasic, IMedia>> GetChildren(Guid id,
int pageNumber = 0,
int pageSize = 0,
string orderBy = "SortOrder",
Direction orderDirection = Direction.Ascending,
bool orderBySystemField = true,
string filter = "")
{
var entity = Services.EntityService.GetByKey(id);
if (entity != null)
{
return GetChildren(entity.Id, pageNumber, pageSize, orderBy, orderDirection, orderBySystemField, filter);
}
throw new HttpResponseException(HttpStatusCode.NotFound);
}
[Obsolete("Do not use this method, use either the overload with INT or GUID instead, this will be removed in future versions")]
[EditorBrowsable(EditorBrowsableState.Never)]
[UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)]
public PagedResult<ContentItemBasic<ContentPropertyBasic, IMedia>> GetChildren(string id,
int pageNumber = 0,
int pageSize = 0,
string orderBy = "SortOrder",
Direction orderDirection = Direction.Ascending,
bool orderBySystemField = true,
string filter = "")
{
foreach (var type in new[] { typeof(int), typeof(Guid) })
{
var parsed = id.TryConvertTo(type);
if (parsed)
{
//oooh magic! will auto select the right overload
return GetChildren((dynamic)parsed.Result);
}
}
throw new HttpResponseException(HttpStatusCode.NotFound);
}
/// <summary>
/// Moves an item to the recycle bin, if it is already there then it will permanently delete it
/// </summary>
@@ -448,12 +516,37 @@ namespace Umbraco.Web.Editors
}
//get the string json from the request
int parentId;
if (int.TryParse(result.FormData["currentFolder"], out parentId) == false)
int parentId; bool entityFound;
string currentFolderId = result.FormData["currentFolder"];
if (int.TryParse(currentFolderId, out parentId) == false)
{
return Request.CreateValidationErrorResponse("The request was not formatted correctly, the currentFolder is not an integer");
// if a guid then try to look up the entity
Guid idGuid;
if (Guid.TryParse(currentFolderId, out idGuid))
{
var entity = Services.EntityService.GetByKey(idGuid);
if (entity != null)
{
entityFound = true;
parentId = entity.Id;
}
else
{
throw new EntityNotFoundException(currentFolderId, "The passed id doesn't exist");
}
}
else
{
return Request.CreateValidationErrorResponse("The request was not formatted correctly, the currentFolder is not an integer or Guid");
}
if (entityFound == false)
{
return Request.CreateValidationErrorResponse("The request was not formatted correctly, the currentFolder is not an integer or Guid");
}
}
//ensure the user has access to this folder by parent id!
if (CheckPermissions(
new Dictionary<string, object>(),
+53 -1
View File
@@ -11,6 +11,11 @@ using System.Net;
using System.Net.Http;
using Umbraco.Web.WebApi;
using Umbraco.Core.Services;
using Umbraco.Core.Models.EntityBase;
using System;
using System.ComponentModel;
using System.Web.Http.Controllers;
using Umbraco.Core;
namespace Umbraco.Web.Editors
{
@@ -24,8 +29,21 @@ namespace Umbraco.Web.Editors
[PluginController("UmbracoApi")]
[UmbracoTreeAuthorize(Constants.Trees.MediaTypes)]
[EnableOverrideAuthorization]
[MediaTypeControllerControllerConfigurationAttribute]
public class MediaTypeController : ContentTypeControllerBase
{
/// <summary>
/// Configures this controller with a custom action selector
/// </summary>
private class MediaTypeControllerControllerConfigurationAttribute : Attribute, IControllerConfiguration
{
public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
{
controllerSettings.Services.Replace(typeof(IHttpActionSelector), new ParameterSwapControllerActionSelector(
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetAllowedChildren", "contentId", typeof(int), typeof(Guid), typeof(string))));
}
}
/// <summary>
/// Constructor
/// </summary>
@@ -170,7 +188,7 @@ namespace Umbraco.Web.Editors
/// <summary>
/// Returns the allowed child content type objects for the content item id passed in
/// Returns the allowed child content type objects for the content item id passed in - based on an INT id
/// </summary>
/// <param name="contentId"></param>
[UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)]
@@ -214,6 +232,40 @@ namespace Umbraco.Web.Editors
return basics;
}
/// <summary>
/// Returns the allowed child content type objects for the content item id passed in - based on a GUID id
/// </summary>
/// <param name="contentId"></param>
[UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)]
public IEnumerable<ContentTypeBasic> GetAllowedChildren(Guid contentId)
{
var entity = ApplicationContext.Services.EntityService.GetByKey(contentId);
if (entity != null)
{
return GetAllowedChildren(entity.Id);
}
throw new HttpResponseException(HttpStatusCode.NotFound);
}
[Obsolete("Do not use this method, use either the overload with INT or GUID instead, this will be removed in future versions")]
[EditorBrowsable(EditorBrowsableState.Never)]
[UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)]
public IEnumerable<ContentTypeBasic> GetAllowedChildren(string contentId)
{
foreach (var type in new[] { typeof(int), typeof(Guid) })
{
var parsed = contentId.TryConvertTo(type);
if (parsed)
{
//oooh magic! will auto select the right overload
return GetAllowedChildren((dynamic)parsed.Result);
}
}
throw new HttpResponseException(HttpStatusCode.NotFound);
}
/// <summary>
/// Move the media type
/// </summary>
@@ -0,0 +1,84 @@
using System;
using System.Linq;
using System.Web;
using System.Web.Http.Controllers;
using Umbraco.Core;
namespace Umbraco.Web.Editors
{
/// <summary>
/// This is used to auto-select specific actions on controllers that would otherwise be ambiguous based on a single parameter type
/// </summary>
/// <remarks>
/// As an example, lets say we have 2 methods: GetChildren(int id) and GetChildren(Guid id), by default Web Api won't allow this since
/// it won't know what to select, but if this Tuple is passed in new Tuple{string, string}("GetChildren", "id")
/// </remarks>
internal class ParameterSwapControllerActionSelector : ApiControllerActionSelector
{
private readonly ParameterSwapInfo[] _actions;
/// <summary>
/// Constructor accepting a list of action name + parameter name
/// </summary>
/// <param name="actions"></param>
public ParameterSwapControllerActionSelector(params ParameterSwapInfo[] actions)
{
_actions = actions;
}
public override HttpActionDescriptor SelectAction(HttpControllerContext controllerContext)
{
var found = _actions.FirstOrDefault(x => controllerContext.Request.RequestUri.GetLeftPart(UriPartial.Path).InvariantEndsWith(x.ActionName));
if (found != null)
{
var id = HttpUtility.ParseQueryString(controllerContext.Request.RequestUri.Query).Get(found.ParamName);
if (id != null)
{
var idTypes = found.SupportedTypes;
foreach (var idType in idTypes)
{
var converted = id.TryConvertTo(idType);
if (converted)
{
var method = MatchByType(idType, controllerContext, found);
if (method != null)
return method;
}
}
}
}
return base.SelectAction(controllerContext);
}
private static ReflectedHttpActionDescriptor MatchByType(Type idType, HttpControllerContext controllerContext, ParameterSwapInfo found)
{
var controllerType = controllerContext.Controller.GetType();
var methods = controllerType.GetMethods().Where(info => info.Name == found.ActionName).ToArray();
if (methods.Length > 1)
{
//choose the one that has the parameter with the T type
var method = methods.FirstOrDefault(x => x.GetParameters().FirstOrDefault(p => p.Name == found.ParamName && p.ParameterType == idType) != null);
return new ReflectedHttpActionDescriptor(controllerContext.ControllerDescriptor, method);
}
return null;
}
internal class ParameterSwapInfo
{
public string ActionName { get; private set; }
public string ParamName { get; private set; }
public Type[] SupportedTypes { get; private set; }
public ParameterSwapInfo(string actionName, string paramName, params Type[] supportedTypes)
{
ActionName = actionName;
ParamName = paramName;
SupportedTypes = supportedTypes;
}
}
}
}
@@ -81,7 +81,7 @@ namespace Umbraco.Web
/// Use focal point, to generate an output image using the focal point instead of the predefined crop
/// </param>
/// <param name="useCropDimensions">
/// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters>.
/// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters.
/// </param>
/// <param name="cacheBuster">
/// Add a serialised date of the last edit of the item to ensure client cache refresh when updated
@@ -81,7 +81,7 @@ namespace Umbraco.Web.Install.InstallSteps
{
var client = new System.Net.WebClient();
var values = new NameValueCollection { { "name", admin.Name }, { "email", admin.Email} };
client.UploadValues("https://umbraco.com/base/Ecom/SubmitEmail/installer.aspx", values);
client.UploadValues("https://shop.umbraco.com/base/Ecom/SubmitEmail/installer.aspx", values);
}
catch { /* fail in silence */ }
}
@@ -147,4 +147,4 @@ namespace Umbraco.Web.Install.InstallSteps
}
}
}
}
}
@@ -3,8 +3,10 @@ using System.Collections.Generic;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.SessionState;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Web.WebApi;
namespace Umbraco.Web.Mvc
{
@@ -93,6 +95,13 @@ namespace Umbraco.Web.Mvc
}
//look in this namespace to create the controller
controllerPluginRoute.DataTokens.Add("Namespaces", new[] {controllerType.Namespace});
//Special case! Check if the controller type implements IRequiresSessionState and if so use our
//custom webapi session handler
if (typeof(IRequiresSessionState).IsAssignableFrom(controllerType))
{
controllerPluginRoute.RouteHandler = new SessionHttpControllerRouteHandler();
}
}
//Don't look anywhere else except this namespace!
@@ -100,9 +109,9 @@ namespace Umbraco.Web.Mvc
//constraints: only match controllers ending with 'controllerSuffixName' and only match this controller's ID for this route
if (controllerSuffixName.IsNullOrWhiteSpace() == false)
{
controllerPluginRoute.Constraints = new RouteValueDictionary(
new Dictionary<string, object>
{
controllerPluginRoute.Constraints = new RouteValueDictionary(
new Dictionary<string, object>
{
{"controller", @"(\w+)" + controllerSuffixName}
});
@@ -28,6 +28,7 @@ using System.Security;
[assembly: System.Security.SecurityRules(System.Security.SecurityRuleSet.Level1)]
[assembly: InternalsVisibleTo("Umbraco.Tests")]
[assembly: InternalsVisibleTo("Umbraco.Tests.Benchmarks")]
[assembly: InternalsVisibleTo("umbraco.MacroEngines")]
[assembly: InternalsVisibleTo("Umbraco.Web.UI")]
[assembly: InternalsVisibleTo("umbraco.webservices")]
@@ -92,7 +92,12 @@ namespace Umbraco.Web.PropertyEditors
//swallow...on purpose, there's a chance that this isn't json and we don't want that to affect
// the website.
}
catch (ArgumentException)
{
//swallow on purpose to prevent this error:
// Can not add Newtonsoft.Json.Linq.JValue to Newtonsoft.Json.Linq.JObject.
}
}
}
}
@@ -313,100 +313,151 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
_parentInitialized = true;
}
private void InitializeNode()
{
if (_xmlNode == null) return;
if (_xmlNode.Attributes != null)
{
_id = int.Parse(_xmlNode.Attributes.GetNamedItem("id").Value);
if (_xmlNode.Attributes.GetNamedItem("key") != null) // because, migration
_key = Guid.Parse(_xmlNode.Attributes.GetNamedItem("key").Value);
if (_xmlNode.Attributes.GetNamedItem("template") != null)
_template = int.Parse(_xmlNode.Attributes.GetNamedItem("template").Value);
if (_xmlNode.Attributes.GetNamedItem("sortOrder") != null)
_sortOrder = int.Parse(_xmlNode.Attributes.GetNamedItem("sortOrder").Value);
if (_xmlNode.Attributes.GetNamedItem("nodeName") != null)
_name = _xmlNode.Attributes.GetNamedItem("nodeName").Value;
if (_xmlNode.Attributes.GetNamedItem("writerName") != null)
_writerName = _xmlNode.Attributes.GetNamedItem("writerName").Value;
if (_xmlNode.Attributes.GetNamedItem("urlName") != null)
_urlName = _xmlNode.Attributes.GetNamedItem("urlName").Value;
// Creatorname is new in 2.1, so published xml might not have it!
try
{
_creatorName = _xmlNode.Attributes.GetNamedItem("creatorName").Value;
}
catch
{
_creatorName = _writerName;
}
//Added the actual userID, as a user cannot be looked up via full name only...
if (_xmlNode.Attributes.GetNamedItem("creatorID") != null)
_creatorId = int.Parse(_xmlNode.Attributes.GetNamedItem("creatorID").Value);
if (_xmlNode.Attributes.GetNamedItem("writerID") != null)
_writerId = int.Parse(_xmlNode.Attributes.GetNamedItem("writerID").Value);
if (UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema)
{
if (_xmlNode.Attributes.GetNamedItem("nodeTypeAlias") != null)
_docTypeAlias = _xmlNode.Attributes.GetNamedItem("nodeTypeAlias").Value;
}
else
{
_docTypeAlias = _xmlNode.Name;
}
if (_xmlNode.Attributes.GetNamedItem("nodeType") != null)
_docTypeId = int.Parse(_xmlNode.Attributes.GetNamedItem("nodeType").Value);
if (_xmlNode.Attributes.GetNamedItem("path") != null)
_path = _xmlNode.Attributes.GetNamedItem("path").Value;
if (_xmlNode.Attributes.GetNamedItem("version") != null)
_version = new Guid(_xmlNode.Attributes.GetNamedItem("version").Value);
if (_xmlNode.Attributes.GetNamedItem("createDate") != null)
_createDate = DateTime.Parse(_xmlNode.Attributes.GetNamedItem("createDate").Value);
if (_xmlNode.Attributes.GetNamedItem("updateDate") != null)
_updateDate = DateTime.Parse(_xmlNode.Attributes.GetNamedItem("updateDate").Value);
if (_xmlNode.Attributes.GetNamedItem("level") != null)
_level = int.Parse(_xmlNode.Attributes.GetNamedItem("level").Value);
_isDraft = (_xmlNode.Attributes.GetNamedItem("isDraft") != null);
}
// load data
var dataXPath = UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema ? "data" : "* [not(@isDoc)]";
var nodes = _xmlNode.SelectNodes(dataXPath);
_contentType = PublishedContentType.Get(PublishedItemType.Content, _docTypeAlias);
var propertyNodes = new Dictionary<string, XmlNode>();
if (nodes != null)
foreach (XmlNode n in nodes)
{
var attrs = n.Attributes;
if (attrs == null) continue;
var alias = UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema
? attrs.GetNamedItem("alias").Value
: n.Name;
propertyNodes[alias.ToLowerInvariant()] = n;
}
_properties = _contentType.PropertyTypes.Select(p =>
{
XmlNode n;
return propertyNodes.TryGetValue(p.PropertyTypeAlias.ToLowerInvariant(), out n)
? new XmlPublishedProperty(p, _isPreviewing, n)
: new XmlPublishedProperty(p, _isPreviewing);
}).Cast<IPublishedProperty>().ToDictionary(
x => x.PropertyTypeAlias,
x => x,
StringComparer.OrdinalIgnoreCase);
private void InitializeNode()
{
InitializeNode(_xmlNode, UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema, _isPreviewing,
out _id, out _key, out _template, out _sortOrder, out _name, out _writerName,
out _urlName, out _creatorName, out _creatorId, out _writerId, out _docTypeAlias, out _docTypeId, out _path,
out _version, out _createDate, out _updateDate, out _level, out _isDraft, out _contentType, out _properties,
PublishedContentType.Get);
// warn: this is not thread-safe...
_nodeInitialized = true;
}
internal static void InitializeNode(XmlNode xmlNode, bool legacy, bool isPreviewing,
out int id, out Guid key, out int template, out int sortOrder, out string name, out string writerName, out string urlName,
out string creatorName, out int creatorId, out int writerId, out string docTypeAlias, out int docTypeId, out string path,
out Guid version, out DateTime createDate, out DateTime updateDate, out int level, out bool isDraft,
out PublishedContentType contentType, out Dictionary<string, IPublishedProperty> properties,
Func<PublishedItemType, string, PublishedContentType> getPublishedContentType)
{
//initialize the out params with defaults:
writerName = null;
docTypeAlias = null;
id = template = sortOrder = template = creatorId = writerId = docTypeId = level = default(int);
key = version = default(Guid);
name = writerName = urlName = creatorName = docTypeAlias = path = null;
createDate = updateDate = default(DateTime);
isDraft = false;
contentType = null;
properties = null;
//return if this is null
if (xmlNode == null)
{
return;
}
if (xmlNode.Attributes != null)
{
id = int.Parse(xmlNode.Attributes.GetNamedItem("id").Value);
if (xmlNode.Attributes.GetNamedItem("key") != null) // because, migration
key = Guid.Parse(xmlNode.Attributes.GetNamedItem("key").Value);
if (xmlNode.Attributes.GetNamedItem("template") != null)
template = int.Parse(xmlNode.Attributes.GetNamedItem("template").Value);
if (xmlNode.Attributes.GetNamedItem("sortOrder") != null)
sortOrder = int.Parse(xmlNode.Attributes.GetNamedItem("sortOrder").Value);
if (xmlNode.Attributes.GetNamedItem("nodeName") != null)
name = xmlNode.Attributes.GetNamedItem("nodeName").Value;
if (xmlNode.Attributes.GetNamedItem("writerName") != null)
writerName = xmlNode.Attributes.GetNamedItem("writerName").Value;
if (xmlNode.Attributes.GetNamedItem("urlName") != null)
urlName = xmlNode.Attributes.GetNamedItem("urlName").Value;
// Creatorname is new in 2.1, so published xml might not have it!
try
{
creatorName = xmlNode.Attributes.GetNamedItem("creatorName").Value;
}
catch
{
creatorName = writerName;
}
//Added the actual userID, as a user cannot be looked up via full name only...
if (xmlNode.Attributes.GetNamedItem("creatorID") != null)
creatorId = int.Parse(xmlNode.Attributes.GetNamedItem("creatorID").Value);
if (xmlNode.Attributes.GetNamedItem("writerID") != null)
writerId = int.Parse(xmlNode.Attributes.GetNamedItem("writerID").Value);
if (legacy)
{
if (xmlNode.Attributes.GetNamedItem("nodeTypeAlias") != null)
docTypeAlias = xmlNode.Attributes.GetNamedItem("nodeTypeAlias").Value;
}
else
{
docTypeAlias = xmlNode.Name;
}
if (xmlNode.Attributes.GetNamedItem("nodeType") != null)
docTypeId = int.Parse(xmlNode.Attributes.GetNamedItem("nodeType").Value);
if (xmlNode.Attributes.GetNamedItem("path") != null)
path = xmlNode.Attributes.GetNamedItem("path").Value;
if (xmlNode.Attributes.GetNamedItem("version") != null)
version = new Guid(xmlNode.Attributes.GetNamedItem("version").Value);
if (xmlNode.Attributes.GetNamedItem("createDate") != null)
createDate = DateTime.Parse(xmlNode.Attributes.GetNamedItem("createDate").Value);
if (xmlNode.Attributes.GetNamedItem("updateDate") != null)
updateDate = DateTime.Parse(xmlNode.Attributes.GetNamedItem("updateDate").Value);
if (xmlNode.Attributes.GetNamedItem("level") != null)
level = int.Parse(xmlNode.Attributes.GetNamedItem("level").Value);
isDraft = (xmlNode.Attributes.GetNamedItem("isDraft") != null);
}
//dictionary to store the property node data
var propertyNodes = new Dictionary<string, XmlNode>();
foreach (XmlNode n in xmlNode.ChildNodes)
{
var e = n as XmlElement;
if (e == null) continue;
if (legacy)
{
if (n.Name == "data")
{
PopulatePropertyNodes(propertyNodes, e, true);
}
else break; //we are not longer on property elements
}
else
{
if (e.HasAttribute("isDoc") == false)
{
PopulatePropertyNodes(propertyNodes, e, false);
}
else break; //we are not longer on property elements
}
}
//lookup the content type and create the properties collection
contentType = getPublishedContentType(PublishedItemType.Content, docTypeAlias);
properties = new Dictionary<string, IPublishedProperty>(StringComparer.OrdinalIgnoreCase);
//fill in the property collection
foreach (var propertyType in contentType.PropertyTypes)
{
XmlNode n;
var val = propertyNodes.TryGetValue(propertyType.PropertyTypeAlias.ToLowerInvariant(), out n)
? new XmlPublishedProperty(propertyType, isPreviewing, n)
: new XmlPublishedProperty(propertyType, isPreviewing);
properties[propertyType.PropertyTypeAlias] = val;
}
}
private static void PopulatePropertyNodes(IDictionary<string, XmlNode> propertyNodes, XmlNode n, bool legacy)
{
var attrs = n.Attributes;
if (attrs == null) return;
var alias = legacy
? attrs.GetNamedItem("alias").Value
: n.Name;
propertyNodes[alias.ToLowerInvariant()] = n;
}
private void InitializeChildren()
{
if (_xmlNode == null) return;
@@ -202,11 +202,13 @@ namespace Umbraco.Web.Trees
/// <returns></returns>
protected override bool HasPathAccess(string id, FormDataCollection queryStrings)
{
var content = Services.ContentService.GetById(int.Parse(id));
if (content == null)
var entity = GetEntityFromId(id);
if (entity == null)
{
return false;
}
IContent content = Services.ContentService.GetById(entity.Id);
return Security.CurrentUser.HasPathAccess(content);
}
@@ -96,7 +96,7 @@ namespace Umbraco.Web.Trees
LogHelper.Warn<ContentTreeControllerBase>("The user " + Security.CurrentUser.Username + " does not have access to the tree node " + id);
return new TreeNodeCollection();
}
// So there's an alt id specified, it's not the root node and the user has access to it, great! But there's one thing we
// need to consider:
// If the tree is being rendered in a dialog view we want to render only the children of the specified id, but
@@ -110,7 +110,7 @@ namespace Umbraco.Web.Trees
id = Constants.System.Root.ToString(CultureInfo.InvariantCulture);
}
}
var entities = GetChildEntities(id);
nodes.AddRange(entities.Select(entity => GetSingleTreeNode(entity, id, queryStrings)).Where(node => node != null));
return nodes;
@@ -122,23 +122,34 @@ namespace Umbraco.Web.Trees
protected IEnumerable<IUmbracoEntity> GetChildEntities(string id)
{
// use helper method to ensure we support both integer and guid lookups
int iid;
// look up from GUID if it's not an integer
if (int.TryParse(id, out iid) == false)
{
throw new InvalidCastException("The id for the media tree must be an integer");
var idEntity = GetEntityFromId(id);
if (idEntity == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
iid = idEntity.Id;
}
//if a request is made for the root node data but the user's start node is not the default, then
// we need to return their start node data
if (iid == Constants.System.Root && UserStartNode != Constants.System.Root)
{
//just return their single start node, it will show up under the 'Content' label
var startNode = Services.EntityService.Get(UserStartNode, UmbracoObjectType);
if (startNode == null)
if (startNode != null)
return new[] { startNode };
else
{
throw new EntityNotFoundException(UserStartNode, "User's start content node could not be found");
}
return new[] { startNode };
}
return Services.EntityService.GetChildren(iid, UmbracoObjectType).ToArray();
@@ -176,9 +187,9 @@ namespace Umbraco.Web.Trees
{
id = altStartId;
}
var nodes = GetTreeNodesInternal(id, queryStrings);
//only render the recycle bin if we are not in dialog and the start id id still the root
if (IsDialog(queryStrings) == false && id == Constants.System.Root.ToInvariantString())
{
@@ -210,8 +221,9 @@ namespace Umbraco.Web.Trees
/// </remarks>
private TreeNodeCollection GetTreeNodesInternal(string id, FormDataCollection queryStrings)
{
IUmbracoEntity current = GetEntityFromId(id);
//before we get the children we need to see if this is a container node
var current = Services.EntityService.Get(int.Parse(id), UmbracoObjectType);
//test if the parent is a listview / container
if (current != null && current.IsContainer())
@@ -286,5 +298,32 @@ namespace Umbraco.Web.Trees
{
return allowedUserOptions.Select(x => x.Action).OfType<ActionBrowse>().Any();
}
/// <summary>
/// Get an entity via an id that can be either an integer or a Guid
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
internal IUmbracoEntity GetEntityFromId(string id)
{
IUmbracoEntity entity;
Guid idGuid = Guid.Empty;
int idInt;
if (Guid.TryParse(id, out idGuid))
{
entity = Services.EntityService.GetByKey(idGuid, UmbracoObjectType);
}
else if (int.TryParse(id, out idInt))
{
entity = Services.EntityService.Get(idInt, UmbracoObjectType);
}
else
{
return null;
}
return entity;
}
}
}

Some files were not shown because too many files have changed in this diff Show More