Compare commits

..

173 Commits

Author SHA1 Message Date
Sebastiaan Jansssen 4e57e1cc18 Nicer variable name, thank to Lee Kelleher! 2018-01-19 12:50:45 +01:00
Sebastiaan Jansssen 410afffaa9 Optimizing use of newlines array 2018-01-19 12:42:04 +01:00
Claus 936e94789d adding to tinyMceConfig.Release.config. 2018-01-19 12:21:57 +01:00
Claus 468a56e5de Merge branch 'karltynan-dev-v7' into dev-v7 2018-01-19 11:11:53 +01:00
Claus 468ec8824a Merge branch 'dev-v7' of git://github.com/karltynan/Umbraco-CMS into karltynan-dev-v7 2018-01-19 11:11:13 +01:00
Sebastiaan Jansssen cad913b7a1 U4-10857 Update to ClientDependency 1.9.5 2018-01-19 08:26:34 +01:00
Sebastiaan Jansssen 382103d99d Bump version to 7.7.9 2018-01-19 08:26:33 +01:00
Sebastiaan Janssen cbb6b9bca0 Merge pull request #2310 from matthew-darby/U4-10495
U4-10495 - Install and customize buttons disabled
2018-01-18 18:38:37 +01:00
Sebastiaan Janssen a1b90a50cd Merge pull request #2343 from soreng/patch-5
Fix for U4-10733
2018-01-18 18:28:34 +01:00
Sebastiaan Janssen 9ea5855073 Merge pull request #2360 from janvanhelvoort/U4-10785
U4-10785 - Filtering of Grid Editors
2018-01-18 18:20:11 +01:00
Sebastiaan Janssen 4a37433042 Merge pull request #2372 from bjarnef/dev-v7-U4-10809
Fix checkmark tree icon, when custom font icon is used for tree icon
2018-01-18 18:00:53 +01:00
Sebastiaan Janssen 91469812d3 Merge pull request #2383 from MMasey/U4-10727
U4 10727 - If nodes with an empty name exist, it's not possible to empty recycle bin
2018-01-18 17:54:40 +01:00
Sebastiaan Janssen 68a8eb7f3f Merge pull request #2375 from redmorello/U4-10762
U4-10762 Fix issue with empty name when registering new members from …
2018-01-18 17:40:05 +01:00
Sebastiaan Janssen 2e7baabb94 Merge pull request #2394 from umbraco/temp-u4-10813
U4-10813 - fix error with groups having a large number of permissions
2018-01-18 17:27:11 +01:00
Sebastiaan Janssen 57745fe71d Merge pull request #2396 from leekelleher/issue/U4-10842
U4-10842 LegacyMediaPickerPropertyConverter null-reference check
2018-01-18 17:17:45 +01:00
Sebastiaan Janssen de6f9236e7 Merge pull request #2399 from leekelleher/issue/U4-8712
U4-8712 MultipleTextstring value newline delimiter changes
2018-01-18 17:13:46 +01:00
Sebastiaan Janssen a00c59e8b5 Merge pull request #2393 from umbraco/temp-u4-10079
U4-10079 - remove default template
2018-01-18 16:54:22 +01:00
Stephan 05c2da807f U4-10079 - fix test 2018-01-18 15:58:51 +01:00
leekelleher 0fd8ca43d6 U4-8712 MultipleTextstring value newline delimiter changes
PR #2185 goes towards resolving this, but doesn't handle the impromptu switch between the `\r\n` and `\n` newline delimiters when republishing a content node.

Using an explicit array of newline chars will resolve this, e.g. `Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None)`

<http://issues.umbraco.org/issue/U4-8712>
2018-01-18 12:18:51 +00:00
leekelleher e466878925 U4-10842 LegacyMediaPickerPropertyConverter ConvertDataToSource null-reference check
If a selected media node has been trashed on the source environment,
when Deploy comes to transfer the node ID, because it can't transfer the media dependency,
the node ID value is set to `null`.

This causes an issue for the `LegacyMediaPickerPropertyConverter`
and `ConvertDataToSource` throws a null-reference exception.

This patch adds in a null-reference check for the `source` value.

<http://issues.umbraco.org/issue/U4-10842>
2018-01-18 10:04:55 +00:00
Mikulas Tomanka 319acc199e Merge pull request #2391 from matthew-darby/U4-10825
U4-10825 Search member by email in member section
2018-01-17 14:18:43 +01:00
Stephan 2a982f21a7 U4-10813 - fix error with groups having a large number of permissions 2018-01-17 13:07:03 +01:00
Stephan 3ff455f11d U4-10079 - remove default template 2018-01-17 11:36:08 +01:00
Matt Darby f49d939a2d U4-10825 Search member by email in member section 2018-01-16 23:06:15 +08:00
Shannon 7e1e83b493 Fixes PackageInstaller event args to make sure the package metadata is passed through 2018-01-16 19:18:08 +11:00
Sebastiaan Janssen 23be0134e7 Merge pull request #1605 from jobf/dev-v7
fix U4-8792 override overflow
2018-01-13 17:57:49 +01:00
Sebastiaan Janssen 355fb64d8b Merge pull request #1573 from drbldgtl/dev-v7-U4-6415
Renamed SerializeToCropDataSet to DeserializeToCropDataSet
2018-01-13 17:33:44 +01:00
Stephan 7f12cff3bd Minor build cleanup 2018-01-11 16:25:18 +01:00
Mads Rasmussen 62d9a1a132 Merge pull request #2379 from umbraco/mzajkowski-U4-4902
U4-4902 Enable 'discard changes' alert throughout the UI
2018-01-11 10:59:22 +01:00
Mads Rasmussen f8c4481eb1 Merge branch 'dev-v7' into mzajkowski-U4-4902 2018-01-11 10:28:28 +01:00
Mike 9f6e6e4701 Add tests to SimilarNodeNameTests to check for null and "" values. 2018-01-10 20:58:10 +00:00
Mike 34ba3f3351 Merge remote-tracking branch 'upstream/dev-v7' into U4-10727 2018-01-10 20:48:28 +00:00
Mike dcfb8b94d8 Refactor SimilarNodeName to account for null nodeName values. 2018-01-10 20:44:30 +00:00
Ilham Boulghallat 700b71f9fb Merge pull request #2377 from abjerner/patch-32
A few fixes for the Danish translation
2018-01-10 15:14:27 +01:00
Robert 3642ff72a4 Removed console.log 2018-01-10 10:03:26 +01:00
Robert 6e830c1171 Wrapping the watch in the elm.focus
Seems to bring the watch calls way down.
2018-01-10 10:03:02 +01:00
Robert ab523baf4f Changed no-dirty-check directive to use scope.$watch to change $pristine to false 2018-01-10 09:24:42 +01:00
Claus 2dec03f441 Merge pull request #2374 from umbraco/temp-U4-10810
U4-10810 Update CDF to support storing TEMP files in environment %tem…
2018-01-10 09:04:58 +01:00
Claus 9ada0d9c97 reverting committed iis settings. 2018-01-10 09:03:35 +01:00
Shannon df553140e8 Updates to latest 1.9.4 of CDF and updates Nuspec 2018-01-10 12:25:24 +11:00
Anders Bjerner 0b898f2e32 A few fixes for the Danish translation 2018-01-09 22:54:48 +01:00
Mads Rasmussen 4c9be2cc49 Merge pull request #2376 from abjerner/patch-31
Start node -> Startnode
2018-01-09 21:32:53 +01:00
Anders Bjerner 1aad4e50d5 Start node -> Startnode 2018-01-09 21:25:36 +01:00
Ian Houghton f5f3676a56 U4-10762 Fix issue with empty name when registering new members from snippet 2018-01-09 15:51:32 +00:00
Shannon bc828fc6f3 U4-10810 Update CDF to support storing TEMP files in environment %temp% location 2018-01-09 13:52:52 +11:00
Mike dc7b83ca6c Add initial test fix for SimilarNodeName when a node has no name. 2018-01-08 17:30:11 +00:00
Robert 8188e4ff55 Merge branch 'U4-4902' of git://github.com/mzajkowski/Umbraco-CMS into mzajkowski-U4-4902 2018-01-08 15:39:29 +01:00
Bjarne Fyrstenborg fcf9dcd803 Fix checkmark tree icon, when custom font icon is used for tree icon, e.g. in UCommerce 2018-01-08 14:40:31 +01:00
Claus f52a84937b Merge pull request #2332 from ruant/U4-10596
fix: add CultureInfo.InvariantCulture to int.Parse
2018-01-08 14:07:10 +01:00
Sebastiaan Jansssen 3556043b55 Update to latest modelsbuilder 2018-01-08 12:44:29 +01:00
Sebastiaan Jansssen 3e8fb11abd Update to latest modelsbuilder 2018-01-08 08:39:01 +01:00
Shannon 5181988a41 forgot to set the flag 2018-01-08 18:10:27 +11:00
Shannon 5169310258 Makes sure we check for index locking on both a normal boot with empty indexes and a cold boot when that is being forced 2018-01-08 17:57:15 +11:00
Shannon Deminick f987640462 Merge pull request #2370 from abjerner/patch-30
Fixed the xmldoc for UserService.Delete
2018-01-08 14:34:53 +11:00
Anders Bjerner 55e97dad74 Fixed the xmldoc for UserService.Delete 2018-01-06 20:24:02 +01:00
Sebastiaan Jansssen d2f3e162f1 Bump version to 7.7.8 2018-01-04 16:45:00 +01:00
Claus 7404e20a19 Merge pull request #2331 from JackPenney/JackPenney-patch-1
Removed CMS Import keys from hebrew translation
2018-01-04 14:05:43 +01:00
Claus 744ff0cf33 Merge pull request #2328 from akierczynska/patch-3
Update pl.xml
2018-01-04 14:04:47 +01:00
Claus afd0018dd6 Merge pull request #2349 from dallastaylor/remove-console-writeline
Remove Console.Writeline() from IsPublishable().
2018-01-04 13:56:43 +01:00
Shannon Deminick c36aec624b Merge pull request #2367 from umbraco/temp-U4-10756-performance-fix
Doesn't fix the issue but improves performance by ~30%
2018-01-04 23:43:25 +11:00
Claus 1ba86f361a Merge pull request #2368 from umbraco/temp-U4-9268
U4-9268 Hidden preview button reappears after 'save' or 'save and pub…
2018-01-04 12:29:00 +01:00
Shannon e5a21625ea U4-9268 Hidden preview button reappears after 'save' or 'save and publish'. 2018-01-04 21:53:55 +11:00
Sebastiaan Janssen efd3d6937a Merge pull request #2365 from umbraco/temp-U4-10701
U4-10701 Umbraco.MemberHasAccess generates slow query after upgrading…
2018-01-03 15:36:51 +01:00
Niels Hartvig 6d46c2204d Doesn't fix the issue but improves performance by ~30% 2018-01-03 12:54:25 +01:00
Warren Buckley 27b9385df5 Update client build NPM dependancy for gulp-connect to use 5.0.0 as next/latest version required you to use CoffeeScript to compile their src code to use the module/component 2018-01-03 11:08:55 +00:00
Shannon ea6d81f7ac U4-10701 Umbraco.MemberHasAccess generates slow query after upgrading to 7.7.6 2018-01-03 11:47:26 +11:00
Mads Rasmussen f56bd731b7 Merge pull request #2227 from abryukhov/U4-10481
U4-10481 - v7.7.x - RU localization of the last added keys
2017-12-27 13:24:42 +01:00
Mads Rasmussen bee0642417 Merge pull request #2263 from krighi/U4-10323
U4-10323 - FIX - Grid stuck at 12 columns (percentage calculation incorrect)
2017-12-27 13:04:31 +01:00
Mads Rasmussen 43abb08e59 Merge pull request #2305 from marcemarc/temp-u4-10637
Fixes U4-10637 - textarea Macro Parameters hidden in niche circumstances
2017-12-27 11:05:12 +01:00
Mads Rasmussen 17db5709f1 Merge pull request #2339 from bjarnef/dev-v7-U4-10720
U4-10720 - Add documentation to umb-table component
2017-12-27 10:38:43 +01:00
Mads Rasmussen 2fc6d9b7df Merge pull request #2327 from bjarnef/dev-v7-U4-10703
Wrap text in umb-node-preview component
2017-12-27 10:19:23 +01:00
Jan van Helvoort 591a881283 Filtering of Grid Editors
If there are more than 15 items, 5 rows of 3, then show the filter.
2017-12-24 15:48:16 +01:00
Shannon e4561851a4 modifies some code changes to null check and not to use extension methods that require singleton access. 2017-12-21 17:30:37 +11:00
Shannon Deminick c6daf2e111 Merge pull request #2353 from mitutee/temp-U4-10770
U4-10770
2017-12-21 17:20:19 +11:00
Sebastiaan Janssen 883366f741 Merge pull request #2295 from JacekJMMJ/temp-u4-10626
Temp u4 10626
2017-12-20 16:07:14 +01:00
Claus cac331adbc U4-10640 Multiple Media Picker duplicate image Angular issue in Umbraco 7.7.4 2017-12-20 13:38:07 +01:00
Shannon Deminick 18318ecabf Merge pull request #2354 from umbraco/temp-u4-10764
U4-10764 - workaround broken events
2017-12-20 17:27:42 +11:00
Shannon 6b9ef6df7a fix tests 2017-12-20 17:02:52 +11:00
Shannon Deminick fcaf78ef29 Merge pull request #2355 from umbraco/temp-u4-10774
U4-10774 - leak in CacheRefresherEventHandler
2017-12-20 16:06:41 +11:00
Sebastiaan Janssen 833b4c552c Merge pull request #2350 from umbraco/temp-U4-10686
U4-10686 TypedMediaAtRoot().Descendants() calls SQL
2017-12-19 18:25:23 +01:00
Mads Rasmussen c678b7f6d9 Merge branch 'bjarnef-dev-v7-U4-10639' into dev-v7 2017-12-19 10:40:59 +01:00
Mads Rasmussen e320840a29 fix position of add button in media picker 2017-12-19 10:39:52 +01:00
Stephan b664fc5e94 U4-10774 - leak in CacheRefresherEventHandler 2017-12-19 10:04:33 +01:00
Stephan b87346bee5 U4-10764 - minor fix 2017-12-19 10:01:51 +01:00
Mads Rasmussen 6029306fe9 Merge branch 'dev-v7-U4-10639' of git://github.com/bjarnef/Umbraco-CMS into bjarnef-dev-v7-U4-10639 2017-12-19 09:53:52 +01:00
Stephan f979792290 U4-10764 - workaround broken events 2017-12-18 16:53:58 +01:00
m.kilianovskyi 5f82fc6e20 Merge branch 'dev-v7' into HEAD 2017-12-18 17:51:27 +02:00
m.kilianovskyi b1a0ca98ea U4-10770 Get all permissions for entity instead of explicit only 2017-12-18 17:50:53 +02:00
Ilham Boulghallat f4f99c2922 Merge pull request #2336 from MrCosmo/temp-U4-10722
U4-10722 fix in Dutch localization
2017-12-18 10:32:33 +00:00
Shannon c1b3da869d U4-10686 TypedMediaAtRoot().Descendants() calls SQL 2017-12-15 16:01:20 +11:00
Dallas Taylor e39158d5e5 Remove Console.Writeline() from IsPublishable(). 2017-12-14 22:18:49 -05:00
Shannon ddd9c8ef7d Do a lock check on the index on startup and unlock it if it is, this is a safety check since we are now using the simplefs dir 2017-12-14 19:25:01 +11:00
Shannon 44db4e7a48 updates nuspec and references to use latest Examine 2017-12-14 18:34:48 +11:00
Shannon f44b158c15 When we disable examine indexing on startup with MainDom, also suspend all indexing 2017-12-14 16:19:54 +11:00
Robert 1ff66d0c3a Merge pull request #2345 from umbraco/temp-U4-10735
U4-10735 Examine update to better manage appdomain shutdowns
2017-12-13 10:52:56 +01:00
Robert aba2c2a79e Merge pull request #2346 from umbraco/temp-U4-10738
U4-10738 Deleting a media type folder causes an exception
2017-12-12 16:02:21 +01:00
Robert 9f62dfc087 Merge pull request #2348 from umbraco/temp-U4-10588
U4-10588 Indexed member data is missing fields
2017-12-12 13:40:48 +01:00
Shannon Deminick a21912dc41 Merge pull request #2337 from skttl/U4-10549-rte-memory-usage
Fix for U4-10549 RTE memory usage
2017-12-12 11:53:42 +01:00
Robert 733b4a770f Merge pull request #2347 from umbraco/temp-U4-10587
U4-10587 Email field in InternalMemberIndex seems to be attempted to …
2017-12-12 11:29:10 +01:00
Shannon 2e097b7e46 U4-10588 Indexed member data is missing fields 2017-12-12 11:25:53 +01:00
Shannon e1b6997ef7 U4-10587 Email field in InternalMemberIndex seems to be attempted to being added to the index twice 2017-12-11 15:50:27 +01:00
Sebastiaan Jansssen b6b7b9f433 Bump version to 7.7.7 2017-12-11 13:57:31 +01:00
Shannon 8181f88cb0 Do not keep looking up data to index if the app is shutting down 2017-12-08 09:09:14 +01:00
Claus 3a4865ef7d U4-10738 Deleting a media type folder causes an exception 2017-12-07 13:45:49 +01:00
Shannon 37f6dbfa6b more logging 2017-12-06 15:46:35 +01:00
Shannon 131fea3ba4 updates some proj refs 2017-12-06 15:38:00 +01:00
Shannon b0661218a0 U4-10735 Examine update to better manage appdomain shutdowns 2017-12-06 15:21:38 +01:00
Søren Gregersen 50335723b0 Fix for U4-10733 2017-12-06 10:09:40 +01:00
Mikkel Holck Madsen fb99b101d8 Merge pull request #2340 from umbraco/temp-U4-10610
U4-10610 Cannot restore content templates
2017-12-05 13:47:11 +01:00
Claus 0ce7efc978 U4-10610 Cannot restore content templates 2017-12-05 10:19:12 +01:00
Bjarne Fyrstenborg 3b8a2d7d65 Add basic documentation to umbPasswordToggle 2017-11-30 23:48:41 +01:00
Bjarne Fyrstenborg 61b9ab5c7b Fix typo 2017-11-30 23:47:55 +01:00
Bjarne Fyrstenborg cfda04b6ed Add documentation of umb-table component 2017-11-30 23:47:23 +01:00
Mikkel Holck Madsen 11bdfbba19 Merge pull request #2338 from umbraco/temp-u4-10715
U4-10715 - scheduled publishing and LB issue
2017-11-30 10:57:12 +01:00
Stephan 9694831638 U4-10715 - scheduled publishing and LB issue 2017-11-30 08:54:16 +01:00
Søren Kottal a20844b55b Destroy tineMceEditors on $destroy 2017-11-29 12:16:12 +01:00
Marco 158e40d7f6 U4-10722 fix in Dutch localization 2017-11-29 08:25:04 +01:00
Rune Antonsen fe67a09ccb fix: add CultureInfo.InvariantCulture to int.Parse
Fix issue where different Culture some times makes int.Parse thrown an `System.FormatException: 'Input string was not in a correct format.' `

Closes: U4-10596
2017-11-23 15:12:01 +01:00
Jack Penney 1c4e1db054 Removed CMS Import keys from hebrew translation 2017-11-23 12:28:17 +00:00
Karl Tynan 58697eaa04 Merge pull request #1 from karltynan/karltynan-patch-1
Added small to allowed TinyMCE valid elements
2017-11-21 15:20:06 +00:00
Karl Tynan ce58ad91b5 Added `small` to allowed TinyMCE valid elements
We use ``small`` a lot in the RTE, and ``small`` is now considered a really useful tag in HTML5. I would like to see this in the default valid elements list for people to use out-of-the box.
2017-11-21 15:18:42 +00:00
Bjarne Fyrstenborg 3c98d9d2e1 Wrap text in umb-node-preview component 2017-11-20 19:18:57 +01:00
bjarnef 8d14a3ef2c added number validation in embed overlay 2017-11-20 12:36:12 +01:00
bjarnef 2f3d9c9509 localize texts 2017-11-20 12:32:09 +01:00
Robert 6e8f3b6e13 Merge pull request #2324 from umbraco/temp-UAASSSCRUM-1254
Added custom RollingFileAppender and changed rollingStyle to "size"
2017-11-17 13:03:48 +01:00
mikkelhm b9cbdbd9a7 Add rename of Appender to csproj 2017-11-17 12:01:32 +01:00
mikkelhm ebf35356a8 Updates the CustomRollingFileAppender to read config settings to control it 2017-11-17 12:00:42 +01:00
akierczynska f3ba1fa7ed Update pl.xml
Adding translations and adjusting pl.xml to en.xml format.
2017-11-15 18:07:31 +00:00
Sebastiaan Janssen 7eca99e249 Bump version to 7.7.6 2017-11-15 15:21:25 +01:00
Sebastiaan Janssen 837bb83d48 Merge remote-tracking branch 'origin/dev-v7.6' into dev-v7 2017-11-15 14:40:19 +01:00
Sebastiaan Janssen 054ac78970 Revert "Position changed from "target" to "center""
This reverts commit c1769aa299.
2017-11-15 14:39:09 +01:00
Robert 5982a868ea Added custom RollingFileAppender and changed rollingStyle to "size"
OpenFile method has been overiden, this means that everytime a log will be opened, it will check the last access time of all existing logs, and delete the ones that haven't been accessed in more than 1 month.

Changing the rollingStyle to "size" will make sure that logs will roll once the maximumFileSize reaches its set limit.
2017-11-14 12:01:11 +01:00
Sebastiaan Janssen 69a42e26a9 Merge remote-tracking branch 'origin/dev-v7.6' into dev-v7 2017-11-14 11:42:00 +01:00
Sebastiaan Janssen d9ed420ed4 U4-10492 Users failing to list with MySQL 2017-11-14 11:38:45 +01:00
Sebastiaan Janssen 2b3397120f Disambiguate events by explicitly naming them 2017-11-14 11:23:48 +01:00
Sebastiaan Janssen 1f41aa7d81 Merge branch 'dev-v7.6' into dev-v7
# Conflicts:
#	src/SolutionInfo.cs
#	src/Umbraco.Core/Configuration/UmbracoVersion.cs
#	src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
#	src/Umbraco.Web.UI/packages.config
2017-11-14 07:19:08 +01:00
Sebastiaan Janssen dad77fc506 Bump version to 7.7.5 2017-11-14 07:17:19 +01:00
Sebastiaan Janssen 350f5c88c7 Bump version to 7.6.12 2017-11-14 07:16:14 +01:00
Stephan 928c0dc535 Merge pull request #2314 from umbraco/temp-U4-10646
U4-10646 Sorting nodes in edge cases can lead to blanking out of property data
2017-11-13 17:33:27 +01:00
Sebastiaan Janssen cc7a28db65 Update to latest ImageProcessor 2017-11-13 15:39:29 +01:00
Sebastiaan Janssen 065e764957 Update to latest Examine 2017-11-13 15:39:28 +01:00
Sebastiaan Janssen 000ba73887 Merge branch 'dev-v7.6' into dev-v7
# Conflicts:
#	build/NuSpecs/UmbracoCms.Core.nuspec
#	src/Umbraco.Tests/Umbraco.Tests.csproj
#	src/Umbraco.Tests/packages.config
#	src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
#	src/Umbraco.Web.UI/packages.config
#	src/Umbraco.Web/Umbraco.Web.csproj
#	src/Umbraco.Web/packages.config
#	src/UmbracoExamine/UmbracoExamine.csproj
#	src/UmbracoExamine/packages.config
#	src/umbraco.MacroEngines/packages.config
#	src/umbraco.MacroEngines/umbraco.MacroEngines.csproj
2017-11-13 15:38:59 +01:00
Sebastiaan Janssen 08b81d991b Update to latest ImageProcessor 2017-11-13 15:33:54 +01:00
Sebastiaan Janssen 4937f87c0d Update to latest Examine 2017-11-13 15:27:15 +01:00
Claus dc67d82671 adding trim. 2017-11-08 12:08:30 +01:00
Claus c7d9683fc4 Merge pull request #2277 from umbraco/temp-U4-10589
U4-10589 Login should be sanitized and/or cause validation errors if trying to save with
2017-11-08 12:07:34 +01:00
Mads Rasmussen ed7675bdcd Merge pull request #1584 from readingdancer/U4-9144
Style changes to the developer dashboard area.
2017-11-08 11:49:48 +01:00
Mads Rasmussen 3c7c6b4ad7 Update thumbnails.less
Use new color variables
2017-11-08 11:38:32 +01:00
Mads Rasmussen 838d7975c9 Update healthcheck.less
prevent UI from "jumping" on hover
2017-11-08 11:36:23 +01:00
Mads Rasmussen 5af9712d51 Merge branch 'dev-v7' into U4-9144 2017-11-08 10:43:58 +01:00
Marcin Zajkowski e14aba3875 More no-dirty-checks and code polishment 2017-11-07 23:21:50 +01:00
Sebastiaan Janssen 579c572dd8 Oops, missed that I was passing a string array into the int array param 2017-11-07 18:36:37 +01:00
Sebastiaan Janssen bbf4c18825 U4-10646 Sorting nodes in edge cases can lead to blanking out of property data 2017-11-07 17:12:04 +01:00
Matt Darby 77162e4b51 Move val-email attribute before required attribute 2017-11-07 19:17:42 +07:00
Bjarne Fyrstenborg e72758e04a Ensure box is square when picker is multiple picker 2017-11-06 22:01:35 +01:00
Bjarne Fyrstenborg 9d373c194d Show thumbnails if any images/files has been selected 2017-11-06 21:42:01 +01:00
Bjarne Fyrstenborg 856e123ec2 Oops - remove duplicated disableFolderSelect 2017-11-06 19:23:41 +01:00
Bjarne Fyrstenborg 0b2af6b48f Fix square image in imagepicker prevalue editor. Cleanup and consistency 2017-11-06 19:12:27 +01:00
Marc Goodson 360665c082 Fixes accidental hiding of textarea parameters when inserting a Macro(with those parameters) via the Rich Text Editor Macro button within a grid cell - U4-10637 2017-11-05 10:36:14 +00:00
Alexander Bryukhov ff98b5f321 U4-10481 - v7.7.x - RU localization of the last added keys (grid editor U4-5722) 2017-11-03 12:18:24 +07:00
Marcin Zajkowski 0df7767f4a Added no-dirty-check to search fields in all dialogs 2017-11-02 17:32:20 +01:00
Jacek Malinowski 92378095a2 U4 10-626. Detecting new Application Url and _umbracoApplicationUrl refresh. 2017-11-02 09:08:46 -07:00
Jacek Malinowski daa0741802 U4-10626 Detecting deplayoment Id change 2017-11-02 07:40:53 -07:00
Stephan eaa8ca5fdc U4-10626 - support resetting UmbracoApplicationUrl 2017-11-02 13:30:45 +01:00
Robert 73b2d31298 Moved username trimming to service level, also added email trimming 2017-11-01 13:08:17 +01:00
Robert 8676a99b3a Trimming login input on the server-side as well 2017-11-01 09:00:05 +01:00
Robert 0b2cfe956d Triming login field to avoid validation errors 2017-10-31 15:19:06 +01:00
Kaue Righi e0e9823a03 U4-10323 - Grid stuck at 12 columns (percentage calculation incorrect) - fix 2017-10-25 11:40:46 -02:00
Alexander Bryukhov 3a98d63bf1 U4-10481v7.7.2 - RU localization of the last added keys (user section filters & sort) 2017-09-29 15:15:28 +07:00
Alexander Bryukhov ac65af772a U4-10481 - v7.7.2 - RU localization of the last added keys 2017-09-29 13:27:45 +07:00
jf 7566546fd7 fix U4-8792 override overflow 2016-11-03 17:32:24 +00:00
Chris Houston a4f83e0125 Style changes to the developer dashboard area. 2016-11-03 14:41:41 +00:00
Shane Marsden 59b8b397b7 Renamed SerializeToCropDataSet to DeserializeToCropDataSet 2016-11-03 11:33:00 +00:00
370 changed files with 3253 additions and 8895 deletions
@@ -239,18 +239,18 @@ function Prepare-Tests
# data
Write-Host "Copy data files"
if( -Not (Test-Path -Path "$tmp\tests\Packaging" ) )
if (-Not (Test-Path -Path "$tmp\tests\Packaging" ) )
{
Write-Host "Create packaging directory"
New-Item -ItemType directory -Path "$tmp\tests\Packaging"
mkdir "$tmp\tests\Packaging" > $null
}
Copy-Files "$src\Umbraco.Tests\Packaging\Packages" "*" "$tmp\tests\Packaging\Packages"
# required for package install tests
if( -Not (Test-Path -Path "$tmp\tests\bin" ) )
if (-Not (Test-Path -Path "$tmp\tests\bin" ) )
{
Write-Host "Create bin directory"
New-Item -ItemType directory -Path "$tmp\tests\bin"
mkdir "$tmp\tests\bin" > $null
}
}
+4 -3
View File
@@ -27,18 +27,19 @@
<dependency id="MiniProfiler" version="[2.1.0, 3.0.0)" />
<dependency id="HtmlAgilityPack" version="[1.4.9.5, 2.0.0)" />
<dependency id="Lucene.Net" version="[2.9.4.1, 3.0.0.0)" />
<dependency id="SharpZipLib" version="[0.86.0, 1.0.0)" />
<dependency id="MySql.Data" version="[6.9.9, 7.0.0)" />
<dependency id="ClientDependency" version="[1.9.2, 2.0.0)" />
<dependency id="ClientDependency" version="[1.9.5, 2.0.0)" />
<dependency id="ClientDependency-Mvc5" version="[1.8.0.0, 2.0.0)" />
<dependency id="AutoMapper" version="[3.3.1, 4.0.0)" />
<dependency id="Newtonsoft.Json" version="[10.0.2, 11.0.0)" />
<dependency id="Examine" version="[0.1.85, 1.0.0)" />
<dependency id="Examine" version="[0.1.89, 1.0.0)" />
<dependency id="ImageProcessor" version="[2.5.6, 3.0.0)" />
<dependency id="ImageProcessor.Web" version="[4.8.7, 5.0.0)" />
<dependency id="semver" version="[1.1.2, 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.7, 2.0.0)" />
<dependency id="System.Threading.Tasks.Dataflow" version="[4.7.0, 5.0.0)" />
<dependency id="System.Threading.Tasks.Dataflow" version="[4.7.0, 5.0.0)" />
</dependencies>
</metadata>
<files>
+1 -1
View File
@@ -17,7 +17,7 @@
<dependencies>
<dependency id="UmbracoCms.Core" version="[$version$]" />
<dependency id="Newtonsoft.Json" version="[10.0.2, 11.0.0)" />
<dependency id="Umbraco.ModelsBuilder" version="[3.0.7, 4.0.0)" />
<dependency id="Umbraco.ModelsBuilder" version="[3.0.8, 4.0.0)" />
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.2.1, 3.0.0)" />
<dependency id="ImageProcessor.Web.Config" version="[2.3.1, 3.0.0)" />
</dependencies>
-2
View File
@@ -49,11 +49,9 @@
<Reference Include="System.Core" />
<Reference Include="System.Data.SqlServerCe, Version=4.0.0.1, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Data.SqlServerCe.Entity, Version=4.0.0.1, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.Entity.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
+2 -2
View File
@@ -11,5 +11,5 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.8.0")]
[assembly: AssemblyInformationalVersion("7.8.0-beta")]
[assembly: AssemblyFileVersion("7.7.9")]
[assembly: AssemblyInformationalVersion("7.7.9")]
+15 -49
View File
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Threading;
using Semver;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.ObjectResolution;
@@ -261,7 +261,7 @@ namespace Umbraco.Core
/// - http://issues.umbraco.org/issue/U4-5728
/// - http://issues.umbraco.org/issue/U4-5391
/// </remarks>
internal string UmbracoApplicationUrl
public string UmbracoApplicationUrl
{
get
{
@@ -269,9 +269,21 @@ namespace Umbraco.Core
return _umbracoApplicationUrl;
}
}
/// <summary>
/// Resets the url.
/// </summary>
public void ResetUmbracoApplicationUrl()
{
_umbracoApplicationUrl = null;
}
// ReSharper disable once InconsistentNaming
internal string _umbracoApplicationUrl;
internal string _umbracoApplicationUrl;
internal List<string> _umbracoApplicationDomains = new List<string>();
internal string _umbracoApplicationDeploymentId;
private Lazy<bool> _configured;
internal MainDom MainDom { get; private set; }
@@ -342,52 +354,6 @@ namespace Umbraco.Core
}
}
/// <summary>
/// Gets the Current Version of the Umbraco Site before an upgrade
/// by using the last/most recent Umbraco Migration that has been run
/// </summary>
/// <returns>A SemVersion of the latest Umbraco DB Migration run</returns>
/// <remarks>
/// NOTE: This existed in the InstallHelper previously but should really be here so it can be re-used if necessary
/// </remarks>
internal SemVersion CurrentVersion()
{
//Set a default version of 0.0.0
var version = new SemVersion(0);
//If we have a db context available, if we don't then we are not installed anyways
if (DatabaseContext.IsDatabaseConfigured && DatabaseContext.CanConnect)
version = DatabaseContext.ValidateDatabaseSchema().DetermineInstalledVersionByMigrations(Services.MigrationEntryService);
if (version != new SemVersion(0))
return version;
// If we aren't able to get a result from the umbracoMigrations table then use the version in web.config, if it's available
if (string.IsNullOrWhiteSpace(GlobalSettings.ConfigurationStatus))
return version;
var configuredVersion = GlobalSettings.ConfigurationStatus;
string currentComment = null;
var current = configuredVersion.Split('-');
if (current.Length > 1)
currentComment = current[1];
Version currentVersion;
if (Version.TryParse(current[0], out currentVersion))
{
version = new SemVersion(
currentVersion.Major,
currentVersion.Minor,
currentVersion.Build,
string.IsNullOrWhiteSpace(currentComment) ? null : currentComment,
currentVersion.Revision > 0 ? currentVersion.Revision.ToString() : null);
}
return version;
}
private void AssertIsNotReady()
{
if (this.IsReady)
@@ -1,18 +0,0 @@
using System.Configuration;
namespace Umbraco.Core.Configuration.UmbracoSettings
{
internal class BackOfficeElement : UmbracoConfigurationElement, IBackOfficeSection
{
[ConfigurationProperty("tours")]
internal TourConfigElement Tours
{
get { return (TourConfigElement)this["tours"]; }
}
ITourSection IBackOfficeSection.Tours
{
get { return Tours; }
}
}
}
@@ -1,7 +0,0 @@
namespace Umbraco.Core.Configuration.UmbracoSettings
{
public interface IBackOfficeSection
{
ITourSection Tours { get; }
}
}
@@ -1,7 +0,0 @@
namespace Umbraco.Core.Configuration.UmbracoSettings
{
public interface ITourSection
{
bool EnableTours { get; }
}
}
@@ -5,8 +5,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
{
public interface IUmbracoSettingsSection : IUmbracoConfigurationSection
{
IBackOfficeSection BackOffice { get; }
IContentSection Content { get; }
ISecuritySection Security { get; }
@@ -1,17 +0,0 @@
using System.Configuration;
namespace Umbraco.Core.Configuration.UmbracoSettings
{
internal class TourConfigElement : UmbracoConfigurationElement, ITourSection
{
//disabled by default so that upgraders don't get it enabled by default
//TODO: we probably just want to disable the initial one from automatically loading ?
[ConfigurationProperty("enable", DefaultValue = false)]
public bool EnableTours
{
get { return (bool)this["enable"]; }
}
//TODO: We could have additional filters, etc... defined here
}
}
@@ -8,12 +8,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
public class UmbracoSettingsSection : ConfigurationSection, IUmbracoSettingsSection
{
[ConfigurationProperty("backOffice")]
internal BackOfficeElement BackOffice
{
get { return (BackOfficeElement)this["backOffice"]; }
}
[ConfigurationProperty("content")]
internal ContentElement Content
{
@@ -155,11 +149,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
get { return Templates; }
}
IBackOfficeSection IUmbracoSettingsSection.BackOffice
{
get { return BackOffice; }
}
IDeveloperSection IUmbracoSettingsSection.Developer
{
get { return Developer; }
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration
{
public class UmbracoVersion
{
private static readonly Version Version = new Version("7.8.0");
private static readonly Version Version = new Version("7.7.9");
/// <summary>
/// Gets the current version of Umbraco.
+34 -34
View File
@@ -21,9 +21,9 @@ namespace Umbraco.Core
public static DateTime TruncateTo(this DateTime dt, DateTruncate truncateTo)
{
if (truncateTo == DateTruncate.Year)
return new DateTime(dt.Year, 0, 0);
return new DateTime(dt.Year, 1, 1);
if (truncateTo == DateTruncate.Month)
return new DateTime(dt.Year, dt.Month, 0);
return new DateTime(dt.Year, dt.Month, 1);
if (truncateTo == DateTruncate.Day)
return new DateTime(dt.Year, dt.Month, dt.Day);
if (truncateTo == DateTruncate.Hour)
@@ -41,43 +41,43 @@ namespace Umbraco.Core
Hour,
Minute,
Second
}
}
/// <summary>
/// Calculates the number of minutes from a date time, on a rolling daily basis (so if
/// Calculates the number of minutes from a date time, on a rolling daily basis (so if
/// date time is before the time, calculate onto next day)
/// </summary>
/// <param name="fromDateTime">Date to start from</param>
/// <param name="scheduledTime">Time to compare against (in Hmm form, e.g. 330, 2200)</param>
/// <returns></returns>
public static int PeriodicMinutesFrom(this DateTime fromDateTime, string scheduledTime)
{
// Ensure time provided is 4 digits long
if (scheduledTime.Length == 3)
{
scheduledTime = "0" + scheduledTime;
}
var scheduledHour = int.Parse(scheduledTime.Substring(0, 2));
var scheduledMinute = int.Parse(scheduledTime.Substring(2));
DateTime scheduledDateTime;
if (IsScheduledInRemainingDay(fromDateTime, scheduledHour, scheduledMinute))
{
scheduledDateTime = new DateTime(fromDateTime.Year, fromDateTime.Month, fromDateTime.Day, scheduledHour, scheduledMinute, 0);
}
else
{
var nextDay = fromDateTime.AddDays(1);
scheduledDateTime = new DateTime(nextDay.Year, nextDay.Month, nextDay.Day, scheduledHour, scheduledMinute, 0);
}
return (int)(scheduledDateTime - fromDateTime).TotalMinutes;
}
private static bool IsScheduledInRemainingDay(DateTime fromDateTime, int scheduledHour, int scheduledMinute)
{
return scheduledHour > fromDateTime.Hour || (scheduledHour == fromDateTime.Hour && scheduledMinute >= fromDateTime.Minute);
/// <returns></returns>
public static int PeriodicMinutesFrom(this DateTime fromDateTime, string scheduledTime)
{
// Ensure time provided is 4 digits long
if (scheduledTime.Length == 3)
{
scheduledTime = "0" + scheduledTime;
}
var scheduledHour = int.Parse(scheduledTime.Substring(0, 2));
var scheduledMinute = int.Parse(scheduledTime.Substring(2));
DateTime scheduledDateTime;
if (IsScheduledInRemainingDay(fromDateTime, scheduledHour, scheduledMinute))
{
scheduledDateTime = new DateTime(fromDateTime.Year, fromDateTime.Month, fromDateTime.Day, scheduledHour, scheduledMinute, 0);
}
else
{
var nextDay = fromDateTime.AddDays(1);
scheduledDateTime = new DateTime(nextDay.Year, nextDay.Month, nextDay.Day, scheduledHour, scheduledMinute, 0);
}
return (int)(scheduledDateTime - fromDateTime).TotalMinutes;
}
private static bool IsScheduledInRemainingDay(DateTime fromDateTime, int scheduledHour, int scheduledMinute)
{
return scheduledHour > fromDateTime.Hour || (scheduledHour == fromDateTime.Hour && scheduledMinute >= fromDateTime.Minute);
}
}
}
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Umbraco.Core.Packaging.Models;
namespace Umbraco.Core.Events
@@ -7,18 +8,27 @@ namespace Umbraco.Core.Events
public class ImportPackageEventArgs<TEntity> : CancellableEnumerableObjectEventArgs<TEntity>, IEquatable<ImportPackageEventArgs<TEntity>>
{
private readonly MetaData _packageMetaData;
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use the overload specifying packageMetaData instead")]
public ImportPackageEventArgs(TEntity eventObject, bool canCancel)
: base(new[] { eventObject }, canCancel)
{
}
public ImportPackageEventArgs(TEntity eventObject, MetaData packageMetaData)
: base(new[] { eventObject })
public ImportPackageEventArgs(TEntity eventObject, MetaData packageMetaData, bool canCancel)
: base(new[] { eventObject }, canCancel)
{
if (packageMetaData == null) throw new ArgumentNullException("packageMetaData");
_packageMetaData = packageMetaData;
}
public ImportPackageEventArgs(TEntity eventObject, MetaData packageMetaData)
: this(eventObject, packageMetaData, true)
{
}
public MetaData PackageMetaData
{
get { return _packageMetaData; }
@@ -76,260 +76,257 @@ namespace Umbraco.Core.Events
{
if (_events == null)
return Enumerable.Empty<IEventDefinition>();
IReadOnlyList<IEventDefinition> events;
switch (filter)
{
case EventDefinitionFilter.All:
return FilterSupersededAndUpdateToLatestEntity(_events);
events = _events;
break;
case EventDefinitionFilter.FirstIn:
var l1 = new OrderedHashSet<IEventDefinition>();
foreach (var e in _events)
{
l1.Add(e);
}
return FilterSupersededAndUpdateToLatestEntity(l1);
events = l1;
break;
case EventDefinitionFilter.LastIn:
var l2 = new OrderedHashSet<IEventDefinition>(keepOldest: false);
foreach (var e in _events)
{
l2.Add(e);
}
return FilterSupersededAndUpdateToLatestEntity(l2);
events = l2;
break;
default:
throw new ArgumentOutOfRangeException("filter", filter, null);
}
return FilterSupersededAndUpdateToLatestEntity(events);
}
private class EventDefinitionTypeData
private class EventDefinitionInfos
{
public IEventDefinition EventDefinition { get; set; }
public Type EventArgType { get; set; }
public SupersedeEventAttribute[] SupersedeAttributes { get; set; }
public Type[] SupersedeTypes { get; set; }
}
/// <summary>
/// This will iterate over the events (latest first) and filter out any events or entities in event args that are included
/// in more recent events that Supersede previous ones. For example, If an Entity has been Saved and then Deleted, we don't want
/// to raise the Saved event (well actually we just don't want to include it in the args for that saved event)
/// </summary>
/// <param name="events"></param>
/// <returns></returns>
private static IEnumerable<IEventDefinition> FilterSupersededAndUpdateToLatestEntity(IReadOnlyList<IEventDefinition> events)
// fixme
// this is way too convoluted, the superceede attribute is used only on DeleteEventargs to specify
// that it superceeds save, publish, move and copy - BUT - publish event args is also used for
// unpublishing and should NOT be superceeded - so really it should not be managed at event args
// level but at event level
//
// what we want is:
// if an entity is deleted, then all Saved, Moved, Copied, Published events prior to this should
// not trigger for the entity - and even though, does it make any sense? making a copy of an entity
// should ... trigger?
//
// not going to refactor it all - we probably want to *always* trigger event but tell people that
// due to scopes, they should not expected eg a saved entity to still be around - however, now,
// going to write a ugly condition to deal with U4-10764
// iterates over the events (latest first) and filter out any events or entities in event args that are included
// in more recent events that Supersede previous ones. For example, If an Entity has been Saved and then Deleted, we don't want
// to raise the Saved event (well actually we just don't want to include it in the args for that saved event)
internal static IEnumerable<IEventDefinition> FilterSupersededAndUpdateToLatestEntity(IReadOnlyList<IEventDefinition> events)
{
//used to keep the 'latest' entity and associated event definition data
var allEntities = new List<Tuple<IEntity, EventDefinitionTypeData>>();
//tracks all CancellableObjectEventArgs instances in the events which is the only type of args we can work with
var cancelableArgs = new List<CancellableObjectEventArgs>();
// keeps the 'latest' entity and associated event data
var entities = new List<Tuple<IEntity, EventDefinitionInfos>>();
// collects the event definitions
// collects the arguments in result, that require their entities to be updated
var result = new List<IEventDefinition>();
var resultArgs = new List<CancellableObjectEventArgs>();
//This will eagerly load all of the event arg types and their attributes so we don't have to continuously look this data up
var allArgTypesWithAttributes = events.Select(x => x.Args.GetType())
// eagerly fetch superceeded arg types for each arg type
var argTypeSuperceeding = events.Select(x => x.Args.GetType())
.Distinct()
.ToDictionary(x => x, x => x.GetCustomAttributes<SupersedeEventAttribute>(false).ToArray());
//Iterate all events and collect the actual entities in them and relates them to their corresponding EventDefinitionTypeData
//we'll process the list in reverse because events are added in the order they are raised and we want to filter out
//any entities from event args that are not longer relevant
//(i.e. if an item is Deleted after it's Saved, we won't include the item in the Saved args)
.ToDictionary(x => x, x => x.GetCustomAttributes<SupersedeEventAttribute>(false).Select(y => y.SupersededEventArgsType).ToArray());
// iterate over all events and filter
//
// process the list in reverse, because events are added in the order they are raised and we want to keep
// the latest (most recent) entities and filter out what is not relevant anymore (too old), eg if an entity
// is Deleted after being Saved, we want to filter out the Saved event
for (var index = events.Count - 1; index >= 0; index--)
{
var eventDefinition = events[index];
var def = events[index];
var argType = eventDefinition.Args.GetType();
var attributes = allArgTypesWithAttributes[eventDefinition.Args.GetType()];
var meta = new EventDefinitionTypeData
var infos = new EventDefinitionInfos
{
EventDefinition = eventDefinition,
EventArgType = argType,
SupersedeAttributes = attributes
EventDefinition = def,
SupersedeTypes = argTypeSuperceeding[def.Args.GetType()]
};
var args = eventDefinition.Args as CancellableObjectEventArgs;
if (args != null)
var args = def.Args as CancellableObjectEventArgs;
if (args == null)
{
var list = TypeHelper.CreateGenericEnumerableFromObject(args.EventObject);
if (list == null)
// not a cancellable event arg, include event definition in result
result.Add(def);
}
else
{
// event object can either be a single object or an enumerable of objects
// try to get as an enumerable, get null if it's not
var eventObjects = TypeHelper.CreateGenericEnumerableFromObject(args.EventObject);
if (eventObjects == null)
{
//extract the event object
var obj = args.EventObject as IEntity;
if (obj != null)
// single object, cast as an IEntity
// if cannot cast, cannot filter, nothing - just include event definition in result
var eventEntity = args.EventObject as IEntity;
if (eventEntity == null)
{
//Now check if this entity already exists in other event args that supersede this current event arg type
if (IsFiltered(obj, meta, allEntities) == false)
{
//if it's not filtered we can adde these args to the response
cancelableArgs.Add(args);
result.Add(eventDefinition);
//track the entity
allEntities.Add(Tuple.Create(obj, meta));
}
result.Add(def);
continue;
}
else
// look for this entity in superceding event args
// found = must be removed (ie not added), else track
if (IsSuperceeded(eventEntity, infos, entities) == false)
{
//Can't retrieve the entity so cant' filter or inspect, just add to the output
result.Add(eventDefinition);
// track
entities.Add(Tuple.Create(eventEntity, infos));
// track result arguments
// include event definition in result
resultArgs.Add(args);
result.Add(def);
}
}
else
{
// enumerable of objects
var toRemove = new List<IEntity>();
foreach (var entity in list)
foreach (var eventObject in eventObjects)
{
//extract the event object
var obj = entity as IEntity;
if (obj != null)
{
//Now check if this entity already exists in other event args that supersede this current event arg type
if (IsFiltered(obj, meta, allEntities))
{
//track it to be removed
toRemove.Add(obj);
}
else
{
//track the entity, it's not filtered
allEntities.Add(Tuple.Create(obj, meta));
}
}
// extract the event object, cast as an IEntity
// if cannot cast, cannot filter, nothing to do - just leave it in the list & continue
var eventEntity = eventObject as IEntity;
if (eventEntity == null)
continue;
// look for this entity in superceding event args
// found = must be removed, else track
if (IsSuperceeded(eventEntity, infos, entities))
toRemove.Add(eventEntity);
else
{
//we don't need to do anything here, we can't cast to IEntity so we cannot filter, so it will just remain in the list
}
entities.Add(Tuple.Create(eventEntity, infos));
}
//remove anything that has been filtered
// remove superceded entities
foreach (var entity in toRemove)
{
list.Remove(entity);
}
eventObjects.Remove(entity);
//track the event and include in the response if there's still entities remaining in the list
if (list.Count > 0)
// if there are still entities in the list, keep the event definition
if (eventObjects.Count > 0)
{
if (toRemove.Count > 0)
{
//re-assign if the items have changed
args.EventObject = list;
// re-assign if changed
args.EventObject = eventObjects;
}
cancelableArgs.Add(args);
result.Add(eventDefinition);
// track result arguments
// include event definition in result
resultArgs.Add(args);
result.Add(def);
}
}
}
else
{
//it's not a cancelable event arg so we just include it in the result
result.Add(eventDefinition);
}
}
//Now we'll deal with ensuring that only the latest(non stale) entities are used throughout all event args
UpdateToLatestEntities(allEntities, cancelableArgs);
// go over all args in result, and update them with the latest instanceof each entity
UpdateToLatestEntities(entities, resultArgs);
//we need to reverse the result since we've been adding by latest added events first!
// reverse, since we processed the list in reverse
result.Reverse();
return result;
}
private static void UpdateToLatestEntities(IEnumerable<Tuple<IEntity, EventDefinitionTypeData>> allEntities, IEnumerable<CancellableObjectEventArgs> cancelableArgs)
// edits event args to use the latest instance of each entity
private static void UpdateToLatestEntities(IEnumerable<Tuple<IEntity, EventDefinitionInfos>> entities, IEnumerable<CancellableObjectEventArgs> args)
{
//Now we'll deal with ensuring that only the latest(non stale) entities are used throughout all event args
// get the latest entities
// ordered hash set + keepOldest will keep the latest inserted entity (in case of duplicates)
var latestEntities = new OrderedHashSet<IEntity>(keepOldest: true);
foreach (var entity in allEntities.OrderByDescending(entity => entity.Item1.UpdateDate))
{
foreach (var entity in entities.OrderByDescending(entity => entity.Item1.UpdateDate))
latestEntities.Add(entity.Item1);
}
foreach (var args in cancelableArgs)
foreach (var arg in args)
{
var list = TypeHelper.CreateGenericEnumerableFromObject(args.EventObject);
if (list == null)
// event object can either be a single object or an enumerable of objects
// try to get as an enumerable, get null if it's not
var eventObjects = TypeHelper.CreateGenericEnumerableFromObject(arg.EventObject);
if (eventObjects == null)
{
//try to find the args entity in the latest entity - based on the equality operators, this will
//match by Id since that is the default equality checker for IEntity. If one is found, than it is
//the most recent entity instance so update the args with that instance so we don't emit a stale instance.
var foundEntity = latestEntities.FirstOrDefault(x => Equals(x, args.EventObject));
// single object
// look for a more recent entity for that object, and replace if any
// works by "equalling" entities ie the more recent one "equals" this one (though different object)
var foundEntity = latestEntities.FirstOrDefault(x => Equals(x, arg.EventObject));
if (foundEntity != null)
{
args.EventObject = foundEntity;
}
arg.EventObject = foundEntity;
}
else
{
// enumerable of objects
// same as above but for each object
var updated = false;
for (int i = 0; i < list.Count; i++)
for (var i = 0; i < eventObjects.Count; i++)
{
//try to find the args entity in the latest entity - based on the equality operators, this will
//match by Id since that is the default equality checker for IEntity. If one is found, than it is
//the most recent entity instance so update the args with that instance so we don't emit a stale instance.
var foundEntity = latestEntities.FirstOrDefault(x => Equals(x, list[i]));
if (foundEntity != null)
{
list[i] = foundEntity;
updated = true;
}
var foundEntity = latestEntities.FirstOrDefault(x => Equals(x, eventObjects[i]));
if (foundEntity == null) continue;
eventObjects[i] = foundEntity;
updated = true;
}
if (updated)
{
args.EventObject = list;
}
arg.EventObject = eventObjects;
}
}
}
/// <summary>
/// This will check against all of the processed entity/events (allEntities) to see if this entity already exists in
/// event args that supersede the event args being passed in and if so returns true.
/// </summary>
/// <param name="entity"></param>
/// <param name="eventDef"></param>
/// <param name="allEntities"></param>
/// <returns></returns>
private static bool IsFiltered(
IEntity entity,
EventDefinitionTypeData eventDef,
List<Tuple<IEntity, EventDefinitionTypeData>> allEntities)
// determines if a given entity, appearing in a given event definition, should be filtered out,
// considering the entities that have already been visited - an entity is filtered out if it
// appears in another even definition, which superceedes this event definition.
private static bool IsSuperceeded(IEntity entity, EventDefinitionInfos infos, List<Tuple<IEntity, EventDefinitionInfos>> entities)
{
var argType = eventDef.EventDefinition.Args.GetType();
//var argType = meta.EventArgsType;
var argType = infos.EventDefinition.Args.GetType();
//check if the entity is found in any processed event data that could possible supersede this one
var foundByEntity = allEntities
.Where(x => x.Item2.SupersedeAttributes.Length > 0
//if it's the same arg type than it cannot supersede
&& x.Item2.EventArgType != argType
&& Equals(x.Item1, entity))
// look for other instances of the same entity, coming from an event args that supercedes other event args,
// ie is marked with the attribute, and is not this event args (cannot supersede itself)
var superceeding = entities
.Where(x => x.Item2.SupersedeTypes.Length > 0 // has the attribute
&& x.Item2.EventDefinition.Args.GetType() != argType // is not the same
&& Equals(x.Item1, entity)) // same entity
.ToArray();
//no args have been processed with this entity so it should not be filtered
if (foundByEntity.Length == 0)
// first time we see this entity = not filtered
if (superceeding.Length == 0)
return false;
// fixme see notes above
// delete event args does NOT superceedes 'unpublished' event
if (argType.IsGenericType && argType.GetGenericTypeDefinition() == typeof(PublishEventArgs<>) && infos.EventDefinition.EventName == "UnPublished")
return false;
// found occurences, need to determine if this event args is superceded
if (argType.IsGenericType)
{
var supercededBy = foundByEntity
.FirstOrDefault(x =>
x.Item2.SupersedeAttributes.Any(y =>
//if the attribute type is a generic type def then compare with the generic type def of the event arg
(y.SupersededEventArgsType.IsGenericTypeDefinition && y.SupersededEventArgsType == argType.GetGenericTypeDefinition())
//if the attribute type is not a generic type def then compare with the normal type of the event arg
|| (y.SupersededEventArgsType.IsGenericTypeDefinition == false && y.SupersededEventArgsType == argType)));
// generic, must compare type arguments
var supercededBy = superceeding.FirstOrDefault(x =>
x.Item2.SupersedeTypes.Any(y =>
// superceeding a generic type which has the same generic type definition
// fixme no matter the generic type parameters? could be different?
y.IsGenericTypeDefinition && y == argType.GetGenericTypeDefinition()
// or superceeding a non-generic type which is ... fixme how is this ever possible? argType *is* generic?
|| y.IsGenericTypeDefinition == false && y == argType));
return supercededBy != null;
}
else
{
var supercededBy = foundByEntity
.FirstOrDefault(x =>
x.Item2.SupersedeAttributes.Any(y =>
//since the event arg type is not a generic type, then we just compare type 1:1
y.SupersededEventArgsType == argType));
// non-generic, can compare types 1:1
var supercededBy = superceeding.FirstOrDefault(x =>
x.Item2.SupersedeTypes.Any(y => y == argType));
return supercededBy != null;
}
}
+2 -2
View File
@@ -26,13 +26,13 @@ namespace Umbraco.Core
var ipAddress = httpContext.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if (string.IsNullOrEmpty(ipAddress))
return httpContext.Request.UserHostAddress;
return httpContext.Request.ServerVariables["REMOTE_ADDR"];
var addresses = ipAddress.Split(',');
if (addresses.Length != 0)
return addresses[0];
return httpContext.Request.UserHostAddress;
return httpContext.Request.ServerVariables["REMOTE_ADDR"];
}
catch (System.Exception ex)
{
@@ -0,0 +1,95 @@
using System;
using System.IO;
using log4net.Appender;
using log4net.Util;
namespace Umbraco.Core.Logging
{
/// <summary>
/// This class will do the exact same thing as the RollingFileAppender that comes from log4net
/// With the extension, that it is able to do automatic cleanup of the logfiles in the directory where logging happens
///
/// By specifying the properties MaxLogFileDays and BaseFilePattern, the files will automaticly get deleted when
/// the logger is configured(typically when the app starts). To utilize this appender swap out the type of the rollingFile appender
/// that ships with Umbraco, to be Umbraco.Core.Logging.RollingFileCleanupAppender, and add the maxLogFileDays and baseFilePattern elements
/// to the configuration i.e.:
///
/// <example>
/// <appender name="rollingFile" type="Log4netAwesomeness.CustomRollingFileAppender, Log4netAwesomeness">
/// <file type="log4net.Util.PatternString" value="App_Data\Logs\UmbracoTraceLog.%property{log4net:HostName}.txt" />
/// <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
/// <appendToFile value="true" />
/// <rollingStyle value="Date" />
/// <maximumFileSize value="5MB" />
/// <maxLogFileDays value="5"/>
/// <basefilePattern value="UmbracoTraceLog.*.txt.*"/>
/// <layout type="log4net.Layout.PatternLayout">
/// <conversionPattern value=" %date [P%property{processId}/D%property{appDomainId}/T%thread] %-5level %logger - %message%newline" />
/// </layout>
/// <layout type="log4net.Layout.PatternLayout">
/// <conversionPattern value=" %date [P%property{processId}/D%property{appDomainId}/T%thread] %-5level %logger - %message%newline" />
/// </layout>
/// <encoding value="utf-8" />
/// </appender>
/// </example>
/// </summary>
public class RollingFileCleanupAppender : RollingFileAppender
{
public int MaxLogFileDays { get; set; }
public string BaseFilePattern { get; set; }
/// <summary>
/// This override will delete logs older than the specified amount of days
/// </summary>
/// <param name="fileName"></param>
/// <param name="append"></param>
protected override void OpenFile(string fileName, bool append)
{
bool cleanup = true;
// Validate settings and input
if (MaxLogFileDays <= 0)
{
LogLog.Warn(typeof(RollingFileCleanupAppender), "Parameter 'MaxLogFileDays' needs to be a positive integer, aborting cleanup");
cleanup = false;
}
if (string.IsNullOrWhiteSpace(BaseFilePattern))
{
LogLog.Warn(typeof(RollingFileCleanupAppender), "Parameter 'BaseFilePattern' is empty, aborting cleanup");
cleanup = false;
}
// grab the directory we are logging to, as this is were we will search for older logfiles
var logFolder = Path.GetDirectoryName(fileName);
if (Directory.Exists(logFolder) == false)
{
LogLog.Warn(typeof(RollingFileCleanupAppender), string.Format("Directory '{0}' for logfiles does not exist, aborting cleanup", logFolder));
cleanup = false;
}
// If everything is validated, we can do the actual cleanup
if (cleanup)
{
Cleanup(logFolder);
}
base.OpenFile(fileName, append);
}
private void Cleanup(string directoryPath)
{
// only take files that matches the pattern we are using i.e. UmbracoTraceLog.*.txt.*
string[] logFiles = Directory.GetFiles(directoryPath, BaseFilePattern);
LogLog.Debug(typeof(RollingFileCleanupAppender), string.Format("Found {0} files that matches the baseFilePattern: '{1}'", logFiles.Length, BaseFilePattern));
foreach (var logFile in logFiles)
{
DateTime lastAccessTime = System.IO.File.GetLastWriteTimeUtc(logFile);
// take the value from the config file
if (lastAccessTime < DateTime.Now.AddDays(-MaxLogFileDays))
{
LogLog.Debug(typeof(RollingFileCleanupAppender), string.Format("Deleting file {0} as its lastAccessTime is older than {1} days speficied by MaxLogFileDays", logFile, MaxLogFileDays));
base.DeleteFile(logFile);
}
}
}
}
}
+1 -13
View File
@@ -2,30 +2,18 @@
namespace Umbraco.Core.Models
{
public sealed class AuditItem : Entity, IAuditItem
public sealed class AuditItem : Entity, IAggregateRoot
{
/// <summary>
/// Constructor for creating an item to be created
/// </summary>
/// <param name="objectId"></param>
/// <param name="comment"></param>
/// <param name="type"></param>
/// <param name="userId"></param>
public AuditItem(int objectId, string comment, AuditType type, int userId)
{
DisableChangeTracking();
Id = objectId;
Comment = comment;
AuditType = type;
UserId = userId;
EnableChangeTracking();
}
public string Comment { get; private set; }
public AuditType AuditType { get; private set; }
public int UserId { get; private set; }
}
}
+4 -2
View File
@@ -95,8 +95,10 @@ namespace Umbraco.Core.Models
get { return _allowedTemplates; }
set
{
SetPropertyValueAndDetectChanges(value, ref _allowedTemplates, Ps.Value.AllowedTemplatesSelector,
Ps.Value.TemplateComparer);
SetPropertyValueAndDetectChanges(value, ref _allowedTemplates, Ps.Value.AllowedTemplatesSelector, Ps.Value.TemplateComparer);
if (_allowedTemplates.Any(x => x.Id == _defaultTemplate) == false)
DefaultTemplateId = 0;
}
}
-12
View File
@@ -1,12 +0,0 @@
using System;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models
{
public interface IAuditItem : IAggregateRoot
{
string Comment { get; }
AuditType AuditType { get; }
int UserId { get; }
}
}
@@ -40,6 +40,19 @@ namespace Umbraco.Core.Models.Identity
identityUser.EnableChangeTracking();
});
config.CreateMap<BackOfficeIdentityUser, UserData>()
.ConstructUsing((BackOfficeIdentityUser user) => new UserData(Guid.NewGuid().ToString("N"))) //this is the 'session id'
.ForMember(detail => detail.Id, opt => opt.MapFrom(user => user.Id))
.ForMember(detail => detail.AllowedApplications, opt => opt.MapFrom(user => user.AllowedSections))
.ForMember(detail => detail.Roles, opt => opt.MapFrom(user => user.Roles.Select(x => x.RoleId).ToArray()))
.ForMember(detail => detail.RealName, opt => opt.MapFrom(user => user.Name))
//When mapping to UserData which is used in the authcookie we want ALL start nodes including ones defined on the groups
.ForMember(detail => detail.StartContentNodes, opt => opt.MapFrom(user => user.CalculatedContentStartNodeIds))
//When mapping to UserData which is used in the authcookie we want ALL start nodes including ones defined on the groups
.ForMember(detail => detail.StartMediaNodes, opt => opt.MapFrom(user => user.CalculatedMediaStartNodeIds))
.ForMember(detail => detail.Username, opt => opt.MapFrom(user => user.UserName))
.ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.Culture))
.ForMember(detail => detail.SessionId, opt => opt.MapFrom(user => user.SecurityStamp.IsNullOrWhiteSpace() ? Guid.NewGuid().ToString("N") : user.SecurityStamp));
}
private string GetPasswordHash(string storedPass)
+1 -6
View File
@@ -69,11 +69,6 @@ namespace Umbraco.Core.Models.Membership
/// <summary>
/// Will hold the media file system relative path of the users custom avatar if they uploaded one
/// </summary>
string Avatar { get; set; }
/// <summary>
/// A Json blob stored for recording tour data for a user
/// </summary>
string TourData { get; set; }
string Avatar { get; set; }
}
}
+1 -13
View File
@@ -99,7 +99,6 @@ namespace Umbraco.Core.Models.Membership
private string _name;
private string _securityStamp;
private string _avatar;
private string _tourData;
private int _sessionTimeout;
private int[] _startContentIds;
private int[] _startMediaIds;
@@ -133,7 +132,6 @@ namespace Umbraco.Core.Models.Membership
public readonly PropertyInfo SecurityStampSelector = ExpressionHelper.GetPropertyInfo<User, string>(x => x.SecurityStamp);
public readonly PropertyInfo AvatarSelector = ExpressionHelper.GetPropertyInfo<User, string>(x => x.Avatar);
public readonly PropertyInfo TourDataSelector = ExpressionHelper.GetPropertyInfo<User, string>(x => x.TourData);
public readonly PropertyInfo SessionTimeoutSelector = ExpressionHelper.GetPropertyInfo<User, int>(x => x.SessionTimeout);
public readonly PropertyInfo StartContentIdSelector = ExpressionHelper.GetPropertyInfo<User, int[]>(x => x.StartContentIds);
public readonly PropertyInfo StartMediaIdSelector = ExpressionHelper.GetPropertyInfo<User, int[]>(x => x.StartMediaIds);
@@ -466,18 +464,8 @@ namespace Umbraco.Core.Models.Membership
{
get { return _avatar; }
set { SetPropertyValueAndDetectChanges(value, ref _avatar, Ps.Value.AvatarSelector); }
}
}
/// <summary>
/// A Json blob stored for recording tour data for a user
/// </summary>
[DataMember]
public string TourData
{
get { return _tourData; }
set { SetPropertyValueAndDetectChanges(value, ref _tourData, Ps.Value.TourDataSelector); }
}
/// <summary>
/// Gets or sets the session timeout.
/// </summary>
@@ -27,10 +27,5 @@ namespace Umbraco.Core.Models.Rdbms
[NullSetting(NullSetting = NullSettings.NotNull)]
[Length(500)]
public string OriginIdentity { get; set; }
[Column("instructionCount")]
[NullSetting(NullSetting = NullSettings.NotNull)]
[Constraint(Default = 1)]
public int InstructionCount { get; set; }
}
}
@@ -24,5 +24,4 @@ namespace Umbraco.Core.Models.Rdbms
[ResultColumn]
public NodeDto NodeDto { get; set; }
}
}
+1 -1
View File
@@ -32,6 +32,6 @@ namespace Umbraco.Core.Models.Rdbms
[Column("logComment")]
[NullSetting(NullSetting = NullSettings.Null)]
[Length(4000)]
public string Comment { get; set; }
public string Comment { get; set; }
}
}
-29
View File
@@ -1,29 +0,0 @@
using System;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
namespace Umbraco.Core.Models.Rdbms
{
[TableName("cmsMedia")]
[PrimaryKey("versionId", autoIncrement = false)]
[ExplicitColumns]
internal class MediaDto
{
[Column("nodeId")]
[ForeignKey(typeof(ContentDto), Column = "nodeId")]
[ForeignKey(typeof(NodeDto))]
[Index(IndexTypes.UniqueNonClustered, Name = "IX_cmsMedia", ForColumns = "nodeId, versionId, mediaPath")]
public int NodeId { get; set; }
[Column("versionId")]
[PrimaryKeyColumn(AutoIncrement = false)]
public Guid VersionId { get; set; }
[Column("mediaPath")]
[NullSetting(NullSetting = NullSettings.Null)]
public string MediaPath { get; set; }
[ResultColumn]
public ContentVersionDto ContentVersionDto { get; set; }
}
}
@@ -27,7 +27,6 @@ namespace Umbraco.Core.Models.Rdbms
[ForeignKey(typeof(PropertyTypeGroupDto))]
public int? PropertyTypeGroupId { get; set; }
[Index(IndexTypes.NonClustered, Name = "IX_cmsPropertyTypeAlias")]
[Column("Alias")]
public string Alias { get; set; }
+3 -11
View File
@@ -15,7 +15,7 @@ namespace Umbraco.Core.Models.Rdbms
{
UserGroupDtos = new List<UserGroupDto>();
UserStartNodeDtos = new HashSet<UserStartNodeDto>();
}
}
[Column("id")]
[PrimaryKeyColumn(Name = "PK_user")]
@@ -40,7 +40,7 @@ namespace Umbraco.Core.Models.Rdbms
[Column("userPassword")]
[Length(500)]
public string Password { get; set; }
/// <summary>
/// This will represent a JSON structure of how the password has been created (i.e hash algorithm, iterations)
/// </summary>
@@ -95,7 +95,7 @@ namespace Umbraco.Core.Models.Rdbms
[NullSetting(NullSetting = NullSettings.NotNull)]
[Constraint(Default = SystemMethods.CurrentDateTime)]
public DateTime UpdateDate { get; set; }
/// <summary>
/// Will hold the media file system relative path of the users custom avatar if they uploaded one
/// </summary>
@@ -103,14 +103,6 @@ namespace Umbraco.Core.Models.Rdbms
[NullSetting(NullSetting = NullSettings.Null)]
[Length(500)]
public string Avatar { get; set; }
/// <summary>
/// A Json blob stored for recording tour data for a user
/// </summary>
[Column("tourData")]
[NullSetting(NullSetting = NullSettings.Null)]
[SpecialDbType(SpecialDbTypes.NTEXT)]
public string TourData { get; set; }
[ResultColumn]
public List<UserGroupDto> UserGroupDtos { get; set; }
@@ -1,52 +0,0 @@
using System;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
namespace Umbraco.Core.Models.Rdbms
{
[TableName("umbracoUserLogin")]
[PrimaryKey("sessionId", autoIncrement = false)]
[ExplicitColumns]
internal class UserLoginDto
{
[Column("sessionId")]
[PrimaryKeyColumn(AutoIncrement = false)]
public Guid SessionId { get; set; }
[Column("userId")]
[ForeignKey(typeof(UserDto), Name = "FK_umbracoUserLogin_umbracoUser_id")]
public int UserId { get; set; }
/// <summary>
/// Tracks when the session is created
/// </summary>
[Column("loggedInUtc")]
[NullSetting(NullSetting = NullSettings.NotNull)]
public DateTime LoggedInUtc { get; set; }
/// <summary>
/// Updated every time a user's session is validated
/// </summary>
/// <remarks>
/// This allows us to guess if a session is timed out if a user doesn't actively log out
/// and also allows us to trim the data in the table
/// </remarks>
[Column("lastValidatedUtc")]
[NullSetting(NullSetting = NullSettings.NotNull)]
public DateTime LastValidatedUtc { get; set; }
/// <summary>
/// Tracks when the session is removed when the user's account is logged out
/// </summary>
[Column("loggedOutUtc")]
[NullSetting(NullSetting = NullSettings.Null)]
public DateTime? LoggedOutUtc { get; set; }
/// <summary>
/// Logs the IP address of the session if available
/// </summary>
[Column("ipAddress")]
[NullSetting(NullSetting = NullSettings.Null)]
public string IpAddress { get; set; }
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ using Umbraco.Core.Persistence.DatabaseAnnotations;
namespace Umbraco.Core.Models.Rdbms
{
[Obsolete("Table no longer exists as of 7.7 - retained only to support migrations from previous versions")]
[Obsolete("Table no longer exists as of 7.6 - retained only to support migrations from previous versions")]
[TableName("umbracoUserType")]
[PrimaryKey("id")]
[ExplicitColumns]
@@ -188,7 +188,7 @@ namespace Umbraco.Core.Models
Language,
/// <summary>
/// Document
/// Document Blueprint
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.DocumentBlueprint, typeof(IContent))]
[FriendlyName("DocumentBlueprint")]
+2 -1
View File
@@ -30,11 +30,12 @@ namespace Umbraco.Core.Models
/// Tries to lookup the user's gravatar to see if the endpoint can be reached, if so it returns the valid URL
/// </summary>
/// <param name="user"></param>
/// <param name="userService"></param>
/// <param name="staticCache"></param>
/// <returns>
/// A list of 5 different sized avatar URLs
/// </returns>
internal static string[] GetUserAvatarUrls(this IUser user, ICacheProvider staticCache)
internal static string[] GetCurrentUserAvatarUrls(this IUser user, IUserService userService, ICacheProvider staticCache)
{
//check if the user has explicitly removed all avatars including a gravatar, this will be possible and the value will be "none"
if (user.Avatar == "none")
+38 -28
View File
@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.IO.Compression;
using ICSharpCode.SharpZipLib.Zip;
namespace Umbraco.Core.Packaging
{
@@ -14,17 +14,17 @@ namespace Umbraco.Core.Packaging
bool fileFound = false;
string foundDir = null;
ReadZipfileEntries(packageFilePath, entry =>
ReadZipfileEntries(packageFilePath, (entry, stream) =>
{
string fileName = Path.GetFileName(entry.Name);
if (string.IsNullOrEmpty(fileName) == false && fileName.Equals(fileToRead, StringComparison.CurrentCultureIgnoreCase))
if (string.IsNullOrEmpty(fileName) == false &&
fileName.Equals(fileToRead, StringComparison.CurrentCultureIgnoreCase))
{
foundDir = entry.Name.Substring(0, entry.Name.Length - fileName.Length);
fileFound = true;
using (var entryStream = entry.Open())
using (var reader = new StreamReader(entryStream))
using (var reader = new StreamReader(stream))
{
retVal = reader.ReadToEnd();
return false;
@@ -77,7 +77,7 @@ namespace Umbraco.Core.Packaging
var d = sourceDestination.ToDictionary(k => k.Key.ToLower(), v => v.Value);
ReadZipfileEntries(packageFilePath, entry =>
ReadZipfileEntries(packageFilePath, (entry, stream) =>
{
string fileName = (Path.GetFileName(entry.Name) ?? string.Empty).ToLower();
if (fileName == string.Empty) { return true; }
@@ -86,9 +86,8 @@ namespace Umbraco.Core.Packaging
if (string.IsNullOrEmpty(fileName) == false && d.TryGetValue(fileName, out destination))
{
using (var streamWriter = File.Open(destination, FileMode.Create))
using (var entryStream = entry.Open())
{
entryStream.CopyTo(streamWriter);
stream.CopyTo(streamWriter);
}
d.Remove(fileName);
@@ -107,7 +106,7 @@ namespace Umbraco.Core.Packaging
{
var retVal = expectedFiles.ToList();
ReadZipfileEntries(packageFilePath, zipEntry =>
ReadZipfileEntries(packageFilePath, (zipEntry, stream) =>
{
string fileName = Path.GetFileName(zipEntry.Name);
@@ -126,7 +125,7 @@ namespace Umbraco.Core.Packaging
var dictionary = new Dictionary<string, List<string>>();
ReadZipfileEntries(packageFilePath, entry =>
ReadZipfileEntries(packageFilePath, (entry, stream) =>
{
string fileName = (Path.GetFileName(entry.Name) ?? string.Empty).ToLower();
@@ -149,42 +148,53 @@ namespace Umbraco.Core.Packaging
{
CheckPackageExists(packageFilePath);
var files = new HashSet<string>(filesToGet.Select(f => f.ToLowerInvariant()));
var files = new HashSet<string>(filesToGet.Select(f => f.ToLower()));
using (var fs = File.OpenRead(packageFilePath))
using (var zipArchive = new ZipArchive(fs))
using (var fs = File.OpenRead(packageFilePath))
{
foreach (var zipEntry in zipArchive.Entries)
using (var zipInputStream = new ZipInputStream(fs))
{
if (zipEntry.Name.IsNullOrWhiteSpace() && zipEntry.FullName.EndsWith("/")) continue;
if (files.Contains(zipEntry.Name.ToLowerInvariant()))
ZipEntry zipEntry;
while ((zipEntry = zipInputStream.GetNextEntry()) != null)
{
using (var memStream = new MemoryStream())
using (var entryStream = zipEntry.Open())
if (zipEntry.IsDirectory) continue;
if (files.Contains(zipEntry.Name))
{
entryStream.CopyTo(memStream);
memStream.Close();
yield return memStream.ToArray();
using (var memStream = new MemoryStream())
{
zipInputStream.CopyTo(memStream);
yield return memStream.ToArray();
memStream.Close();
}
}
}
zipInputStream.Close();
}
fs.Close();
}
}
private void ReadZipfileEntries(string packageFilePath, Func<ZipArchiveEntry, bool> entryFunc, bool skipsDirectories = true)
private void ReadZipfileEntries(string packageFilePath, Func<ZipEntry, ZipInputStream, bool> entryFunc, bool skipsDirectories = true)
{
CheckPackageExists(packageFilePath);
using (var fs = File.OpenRead(packageFilePath))
using (var zipArchive = new ZipArchive(fs))
{
foreach (var zipEntry in zipArchive.Entries)
using (var zipInputStream = new ZipInputStream(fs))
{
if (zipEntry.Name.IsNullOrWhiteSpace() && zipEntry.FullName.EndsWith("/") && skipsDirectories)
continue;
if (entryFunc(zipEntry) == false) break;
ZipEntry zipEntry;
while ((zipEntry = zipInputStream.GetNextEntry()) != null)
{
if (zipEntry.IsDirectory && skipsDirectories) continue;
if (entryFunc(zipEntry, zipInputStream) == false) break;
}
zipInputStream.Close();
}
fs.Close();
}
}
}
@@ -98,18 +98,18 @@ namespace Umbraco.Core.Persistence.Factories
{
//NOTE Currently doesn't add Alias (legacy that eventually will go away)
var documentDto = new DocumentDto
{
Newest = true,
NodeId = entity.Id,
Published = entity.Published,
Text = entity.Name,
UpdateDate = entity.UpdateDate,
WriterUserId = entity.WriterId,
VersionId = entity.Version,
ExpiresDate = null,
ReleaseDate = null,
ContentVersionDto = BuildContentVersionDto(entity)
};
{
Newest = true,
NodeId = entity.Id,
Published = entity.Published,
Text = entity.Name,
UpdateDate = entity.UpdateDate,
WriterUserId = entity.WriterId,
VersionId = entity.Version,
ExpiresDate = null,
ReleaseDate = null,
ContentVersionDto = BuildContentVersionDto(entity)
};
if (entity.Template != null && entity.Template.Id > 0)
documentDto.TemplateId = entity.Template.Id;
@@ -5,7 +5,6 @@ using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Factories
{
internal class MacroFactory
{
public IMacro BuildEntity(MacroDto dto)
@@ -1,6 +1,5 @@
using System;
using System.Globalization;
using System.Text.RegularExpressions;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Rdbms;
@@ -10,17 +9,20 @@ namespace Umbraco.Core.Persistence.Factories
{
private readonly IMediaType _contentType;
private readonly Guid _nodeObjectTypeId;
private readonly int _id;
private int _primaryKey;
public MediaFactory(IMediaType contentType, Guid nodeObjectTypeId)
public MediaFactory(IMediaType contentType, Guid nodeObjectTypeId, int id)
{
_contentType = contentType;
_nodeObjectTypeId = nodeObjectTypeId;
_id = id;
}
public MediaFactory(Guid nodeObjectTypeId)
public MediaFactory(Guid nodeObjectTypeId, int id)
{
_nodeObjectTypeId = nodeObjectTypeId;
_id = id;
}
#region Implementation of IEntityFactory<IMedia,ContentVersionDto>
@@ -62,27 +64,15 @@ namespace Umbraco.Core.Persistence.Factories
return BuildEntity(dto, _contentType);
}
public MediaDto BuildDto(IMedia entity)
public ContentVersionDto BuildDto(IMedia entity)
{
var versionDto = new ContentVersionDto
{
NodeId = entity.Id,
VersionDate = entity.UpdateDate,
VersionId = entity.Version,
ContentDto = BuildContentDto(entity)
};
//Extract the media path for storage
string mediaPath;
TryMatch(entity.GetValue<string>("umbracoFile"), out mediaPath);
var dto = new MediaDto()
{
NodeId = entity.Id,
ContentVersionDto = versionDto,
MediaPath = mediaPath,
VersionId = entity.Version
};
var dto = new ContentVersionDto
{
NodeId = entity.Id,
VersionDate = entity.UpdateDate,
VersionId = entity.Version,
ContentDto = BuildContentDto(entity)
};
return dto;
}
@@ -129,30 +119,5 @@ namespace Umbraco.Core.Persistence.Factories
return nodeDto;
}
private static readonly Regex MediaPathPattern = new Regex(@"(/media/.+?)(?:['""]|$)", RegexOptions.Compiled);
/// <summary>
/// Try getting a media path out of the string being stored for media
/// </summary>
/// <param name="text"></param>
/// <param name="mediaPath"></param>
/// <returns></returns>
internal static bool TryMatch(string text, out string mediaPath)
{
mediaPath = null;
if (string.IsNullOrWhiteSpace(text))
return false;
var match = MediaPathPattern.Match(text);
if (match.Success == false || match.Groups.Count != 2)
return false;
var url = match.Groups[1].Value;
mediaPath = url;
return true;
}
}
}
@@ -36,7 +36,6 @@ namespace Umbraco.Core.Persistence.Factories
user.Avatar = dto.Avatar;
user.EmailConfirmedDate = dto.EmailConfirmedDate;
user.InvitedDate = dto.InvitedDate;
user.TourData = dto.TourData;
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
@@ -70,8 +69,7 @@ namespace Umbraco.Core.Persistence.Factories
UpdateDate = entity.UpdateDate,
Avatar = entity.Avatar,
EmailConfirmedDate = entity.EmailConfirmedDate,
InvitedDate = entity.InvitedDate,
TourData = entity.TourData
InvitedDate = entity.InvitedDate
};
foreach (var startNodeId in entity.StartContentIds)
@@ -1,47 +0,0 @@
using System.Collections.Concurrent;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Mappers
{
[MapperFor(typeof(AuditItem))]
[MapperFor(typeof(IAuditItem))]
public sealed class AuditMapper : BaseMapper
{
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
public AuditMapper(ISqlSyntaxProvider sqlSyntax) : base(sqlSyntax)
{
}
//NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
// otherwise that would fail because there is no public constructor.
public AuditMapper()
{
BuildMap();
}
#region Overrides of BaseMapper
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache
{
get { return PropertyInfoCacheInstance; }
}
internal override void BuildMap()
{
if (PropertyInfoCache.IsEmpty)
{
CacheMap<AuditItem, LogDto>(src => src.Id, dto => dto.NodeId);
CacheMap<AuditItem, LogDto>(src => src.CreateDate, dto => dto.Datestamp);
CacheMap<AuditItem, LogDto>(src => src.UserId, dto => dto.UserId);
CacheMap<AuditItem, LogDto>(src => src.AuditType, dto => dto.Header);
CacheMap<AuditItem, LogDto>(src => src.Comment, dto => dto.Comment);
}
}
#endregion
}
}
@@ -43,28 +43,27 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
{3, typeof (ContentDto)},
{4, typeof (ContentVersionDto)},
{5, typeof (DocumentDto)},
{6, typeof (MediaDto)},
{7, typeof (ContentTypeTemplateDto)},
{8, typeof (DataTypeDto)},
{9, typeof (DataTypePreValueDto)},
{10, typeof (DictionaryDto)},
{11, typeof (LanguageDto)},
{12, typeof (LanguageTextDto)},
{13, typeof (DomainDto)},
{14, typeof (LogDto)},
{15, typeof (MacroDto)},
{16, typeof (MacroPropertyDto)},
{17, typeof (MemberTypeDto)},
{18, typeof (MemberDto)},
{19, typeof (Member2MemberGroupDto)},
{20, typeof (ContentXmlDto)},
{21, typeof (PreviewXmlDto)},
{22, typeof (PropertyTypeGroupDto)},
{23, typeof (PropertyTypeDto)},
{24, typeof (PropertyDataDto)},
{25, typeof (RelationTypeDto)},
{26, typeof (RelationDto)},
{6, typeof (ContentTypeTemplateDto)},
{7, typeof (DataTypeDto)},
{8, typeof (DataTypePreValueDto)},
{9, typeof (DictionaryDto)},
{10, typeof (LanguageDto)},
{11, typeof (LanguageTextDto)},
{12, typeof (DomainDto)},
{13, typeof (LogDto)},
{14, typeof (MacroDto)},
{15, typeof (MacroPropertyDto)},
{16, typeof (MemberTypeDto)},
{17, typeof (MemberDto)},
{18, typeof (Member2MemberGroupDto)},
{19, typeof (ContentXmlDto)},
{20, typeof (PreviewXmlDto)},
{21, typeof (PropertyTypeGroupDto)},
{22, typeof (PropertyTypeDto)},
{23, typeof (PropertyDataDto)},
{24, typeof (RelationTypeDto)},
{25, typeof (RelationDto)},
{28, typeof (TagDto)},
{29, typeof (TagRelationshipDto)},
@@ -92,7 +91,6 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
{52, typeof (UserGroup2NodePermissionDto) },
{53, typeof (UserGroup2AppDto) },
{54, typeof (UserStartNodeDto) },
{55, typeof (UserLoginDto)}
};
#endregion
@@ -148,12 +148,6 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
return new Version(7, 6, 0);
}
//if the error is for cmsMedia it must be the previous version to 7.8 since that is when it is added
if (Errors.Any(x => x.Item1.Equals("Table") && (x.Item2.InvariantEquals("cmsMedia"))))
{
return new Version(7, 7, 0);
}
return UmbracoVersion.Current;
}
@@ -1,101 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.Factories;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenEightZero
{
[Migration("7.8.0", 1, Constants.System.UmbracoMigrationName)]
public class AddCmsMediaTable : MigrationBase
{
public AddCmsMediaTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger)
{
}
public override void Up()
{
var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray();
if (tables.InvariantContains("cmsMedia") == false)
{
Create.Table<MediaDto>();
MigrateMediaPaths();
}
}
private void MigrateMediaPaths()
{
Execute.Code(database =>
{
//Due to how much data there can be and that this query will result in executing an Index Scan on the cmsPropertyData PK
//which will literally iterate over every row in there (assume there's 1,000,000 and you can see why this will perform
//horribly) ultimately we need to fix this: http://issues.umbraco.org/issue/U4-10286
//so in the meantime I've found an ugly query to work around this Index Scan issue which is to use a sub-query of Ids.
//Doing this results in the same Index Scan taking place but it only visits the explicit rows that are provided, not every row
//in the cmsPropertyData table. This results in a tremendously faster query when there is tons of data.
//The will return any media property in nvarchar or ntext that is an umbracoFile that is not null.
//This will give us the least amount of data returned to work with.
var sql = @"SELECT cmsPropertyData.dataNvarchar, cmsPropertyData.dataNtext, umbracoNode.id, cmsContentVersion.VersionId
FROM cmsPropertyData
INNER JOIN cmsPropertyType ON cmsPropertyType.id = cmsPropertyData.propertytypeid
INNER JOIN umbracoNode ON umbracoNode.id = cmsPropertyData.contentNodeId
INNER JOIN cmsContentVersion ON cmsContentVersion.ContentId = umbracoNode.id
WHERE cmsPropertyType.Alias = (@alias)
AND umbracoNode.id IN (SELECT umbracoNode.id
FROM umbracoNode
INNER JOIN cmsContent ON cmsContent.nodeId = umbracoNode.id
INNER JOIN cmsContentType ON cmsContentType.nodeId = cmsContent.contentType
INNER JOIN cmsPropertyType ON cmsPropertyType.contentTypeId = cmsContentType.nodeId
WHERE cmsPropertyType.Alias = (@alias) AND umbracoNode.nodeObjectType = (@nodeObjectType))
AND (cmsPropertyData.dataNvarchar IS NOT NULL OR cmsPropertyData.dataNtext IS NOT NULL)";
var paths = new List<MediaDto>();
//using QUERY = a db cursor, we won't load this all into memory first, just row by row
foreach (var row in database.Query<dynamic>(sql, new {alias = "umbracoFile", nodeObjectType = Constants.ObjectTypes.Media}))
{
var id = (int) row.id;
var versionId = (Guid)row.VersionId;
string mediaPath = null;
//if there's values in dataNvarchar then ensure there's a media path match and extract it
if (row.dataNvarchar != null && MediaFactory.TryMatch((string) row.dataNvarchar, out mediaPath))
{
paths.Add(new MediaDto
{
MediaPath = mediaPath,
NodeId = id,
VersionId = versionId
});
}
//if there's values in dataNtext then ensure there's a media path match and extract it
else if (row.dataNtext != null && MediaFactory.TryMatch((string) row.dataNtext, out mediaPath))
{
paths.Add(new MediaDto
{
MediaPath = mediaPath,
NodeId = id,
VersionId = versionId
});
}
}
//now we need to insert paths for each item
database.BulkInsertRecords(paths, SqlSyntax);
return null;
});
}
public override void Down()
{
}
}
}
@@ -1,48 +0,0 @@
using System.Linq;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenEightZero
{
[Migration("7.8.0", 0, Constants.System.UmbracoMigrationName)]
public class AddIndexToPropertyTypeAliasColumn : MigrationBase
{
public AddIndexToPropertyTypeAliasColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger)
: base(sqlSyntax, logger)
{ }
public override void Up()
{
Execute.Code(database =>
{
var dbIndexes = SqlSyntax.GetDefinedIndexesDefinitions(Context.Database);
//make sure it doesn't already exist
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsPropertyTypeAlias")) == false)
{
var localContext = new LocalMigrationContext(Context.CurrentDatabaseProvider, database, SqlSyntax, Logger);
//we can apply the index
localContext.Create.Index("IX_cmsPropertyTypeAlias").OnTable("cmsPropertyType")
.OnColumn("Alias")
.Ascending()
.WithOptions()
.NonClustered();
return localContext.GetSql();
}
return null;
});
}
public override void Down()
{
Delete.Index("IX_cmsPropertyTypeAlias").OnTable("cmsPropertyType");
}
}
}
@@ -1,32 +0,0 @@
using System.Linq;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenEightZero
{
[Migration("7.8.0", 2, Constants.System.UmbracoMigrationName)]
public class AddInstructionCountColumn : MigrationBase
{
public AddInstructionCountColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger)
: base(sqlSyntax, logger)
{
}
public override void Up()
{
//Don't exeucte if the column is already there
var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray();
if (columns.Any(x => x.TableName.InvariantEquals("umbracoCacheInstruction") && x.ColumnName.InvariantEquals("instructionCount")) == false)
Create.Column("instructionCount")
.OnTable("umbracoCacheInstruction")
.AsInt32()
.WithDefaultValue(1)
.NotNullable();
}
public override void Down()
{
}
}
}
@@ -1,38 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenEightZero
{
[Migration("7.8.0", 1, Constants.System.UmbracoMigrationName)]
public class AddTourDataUserColumn : MigrationBase
{
public AddTourDataUserColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger)
: base(sqlSyntax, logger)
{
}
public override void Up()
{
//Don't exeucte if the column is already there
var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray();
if (columns.Any(x => x.TableName.InvariantEquals("umbracoUser") && x.ColumnName.InvariantEquals("tourData")) == false)
{
var textType = SqlSyntax.GetSpecialDbType(SpecialDbTypes.NTEXT);
Create.Column("tourData").OnTable("umbracoUser").AsCustom(textType).Nullable();
}
}
public override void Down()
{
}
}
}
@@ -1,29 +0,0 @@
using System.Linq;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenEightZero
{
[Migration("7.8.0", 4, Constants.System.UmbracoMigrationName)]
public class AddUserLoginTable : MigrationBase
{
public AddUserLoginTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger)
{
}
public override void Up()
{
var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray();
if (tables.InvariantContains("umbracoUserLogin") == false)
{
Create.Table<UserLoginDto>();
}
}
public override void Down()
{
}
}
}
@@ -1,5 +1,6 @@
using System;
using System.Data;
using System.Data.SqlClient;
using Umbraco.Core.Persistence.FaultHandling;
namespace Umbraco.Core.Persistence
@@ -1,12 +1,16 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Data.SqlServerCe;
using System.Linq;
using System.Text.RegularExpressions;
using MySql.Data.MySqlClient;
using StackExchange.Profiling.Data;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence
@@ -38,12 +38,8 @@ namespace Umbraco.Core.Persistence
_tableDefinition = DefinitionFactory.GetTableDefinition(sqlSyntaxProvider, pd.type);
if (_tableDefinition == null) throw new InvalidOperationException("No table definition found for type " + pd.type);
//Only return real columns, do not include columns that are result columns
_readerColumns = pd.Columns
.Where(x => x.Value.ResultColumn == false)
.Select(x => x.Value)
.ToArray();
_readerColumns = pd.Columns.Select(x => x.Value).ToArray();
_sqlSyntaxProvider = sqlSyntaxProvider;
_enumerator = dataSource.GetEnumerator();
_columnDefinitions = _tableDefinition.Columns.ToArray();
@@ -1,113 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Persistence.Repositories
{
internal class AuditRepository : PetaPocoRepositoryBase<int, IAuditItem>, IAuditRepository
internal class AuditRepository : PetaPocoRepositoryBase<int, AuditItem>, IAuditRepository
{
public AuditRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
: base(work, cache, logger, sqlSyntax)
{
}
/// <summary>
/// Return the audit items as paged result
/// </summary>
/// <param name="query">
/// The query coming from the service
/// </param>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="totalRecords"></param>
/// <param name="orderDirection"></param>
/// <param name="auditTypeFilter">
/// Since we currently do not have enum support with our expression parser, we cannot query on AuditType in the query or the custom filter
/// so we need to do that here
/// </param>
/// <param name="customFilter">
/// A user supplied custom filter
/// </param>
/// <returns></returns>
public IEnumerable<IAuditItem> GetPagedResultsByQuery(IQuery<IAuditItem> query, long pageIndex, int pageSize,
out long totalRecords, Direction orderDirection,
AuditType[] auditTypeFilter,
IQuery<IAuditItem> customFilter)
protected override void PersistNewItem(AuditItem entity)
{
if (auditTypeFilter == null) auditTypeFilter = new AuditType[0];
var sql = GetBaseQuery(false);
if (query == null) query = new Query<IAuditItem>();
var translatorIds = new SqlTranslator<IAuditItem>(sql, query);
var translatedQuery = translatorIds.Translate();
var customFilterWheres = customFilter != null ? customFilter.GetWhereClauses().ToArray() : null;
var hasCustomFilter = customFilterWheres != null && customFilterWheres.Length > 0;
if (hasCustomFilter)
{
var filterSql = new Sql();
var first = true;
foreach (var filterClaus in customFilterWheres)
{
if (first == false)
{
filterSql.Append(" AND ");
}
filterSql.Append(string.Format("({0})", filterClaus.Item1), filterClaus.Item2);
first = false;
}
translatedQuery = GetFilteredSqlForPagedResults(translatedQuery, filterSql);
}
if (auditTypeFilter.Length > 0)
{
var filterSql = new Sql();
var first = true;
foreach (var filterClaus in auditTypeFilter)
{
if (first == false || hasCustomFilter)
{
filterSql.Append(" AND ");
}
filterSql.Append("(logHeader = @logHeader)", new {logHeader = filterClaus.ToString() });
first = false;
}
translatedQuery = GetFilteredSqlForPagedResults(translatedQuery, filterSql);
}
if (orderDirection == Direction.Descending)
translatedQuery.OrderByDescending("Datestamp");
else
translatedQuery.OrderBy("Datestamp");
// Get page of results and total count
var pagedResult = Database.Page<LogDto>(pageIndex + 1, pageSize, translatedQuery);
totalRecords = pagedResult.TotalItems;
var pages = pagedResult.Items.Select(
dto => new AuditItem(dto.Id, dto.Comment, Enum<AuditType>.Parse(dto.Header), dto.UserId)).ToArray();
//Mapping the DateStamp
for (int i = 0; i < pages.Length; i++)
{
pages[i].CreateDate = pagedResult.Items[i].Datestamp;
}
return pages;
throw new NotImplementedException();
}
protected override void PersistUpdatedItem(IAuditItem entity)
#region Not Implemented - not needed
protected override void PersistUpdatedItem(AuditItem entity)
{
Database.Insert(new LogDto
{
@@ -119,40 +35,26 @@ namespace Umbraco.Core.Persistence.Repositories
});
}
protected override AuditItem PerformGet(int id)
{
throw new NotImplementedException();
}
protected override IEnumerable<AuditItem> PerformGetAll(params int[] ids)
{
throw new NotImplementedException();
}
protected override IEnumerable<AuditItem> PerformGetByQuery(IQuery<AuditItem> query)
{
throw new NotImplementedException();
}
protected override Sql GetBaseQuery(bool isCount)
{
var sql = new Sql()
.Select(isCount ? "COUNT(*)" : "umbracoLog.id, umbracoLog.userId, umbracoLog.NodeId, umbracoLog.Datestamp, umbracoLog.logHeader, umbracoLog.logComment, umbracoUser.userName, umbracoUser.avatar as userAvatar")
.From<LogDto>(SqlSyntax);
if (isCount == false)
{
sql = sql.LeftJoin<UserDto>(SqlSyntax).On<UserDto, LogDto>(SqlSyntax, dto => dto.Id, dto => dto.UserId);
}
return sql;
}
#region Not Implemented - not needed currently
protected override void PersistNewItem(IAuditItem entity)
{
throw new NotImplementedException();
}
protected override IAuditItem PerformGet(int id)
{
throw new NotImplementedException();
}
protected override IEnumerable<IAuditItem> PerformGetAll(params int[] ids)
{
throw new NotImplementedException();
}
protected override IEnumerable<IAuditItem> PerformGetByQuery(IQuery<IAuditItem> query)
{
throw new NotImplementedException();
}
protected override string GetBaseWhereClause()
{
throw new NotImplementedException();
@@ -169,33 +71,6 @@ namespace Umbraco.Core.Persistence.Repositories
}
#endregion
private Sql GetFilteredSqlForPagedResults(Sql sql, Sql filterSql)
{
Sql filteredSql;
// Apply filter
if (filterSql != null)
{
var sqlFilter = " WHERE " + filterSql.SQL.TrimStart("AND ");
//NOTE: this is certainly strange - NPoco handles this much better but we need to re-create the sql
// instance a couple of times to get the parameter order correct, for some reason the first
// time the arguments don't show up correctly but the SQL argument parameter names are actually updated
// accordingly - so we re-create it again. In v8 we don't need to do this and it's already taken care of.
filteredSql = new Sql(sql.SQL, sql.Arguments);
var args = filteredSql.Arguments.Concat(filterSql.Arguments).ToArray();
filteredSql = new Sql(
string.Format("{0} {1}", filteredSql.SQL, sqlFilter),
args);
filteredSql = new Sql(filteredSql.SQL, args);
}
else
{
//copy to var so that the original isn't changed
filteredSql = new Sql(sql.SQL, sql.Arguments);
}
return filteredSql;
}
}
}
@@ -901,7 +901,7 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
public void AddOrUpdatePermissions(ContentPermissionSet permission)
{
_permissionRepository.AddOrUpdate(permission);
}
}
/// <summary>
/// Used to remove the content xml for a content item
@@ -1075,8 +1075,8 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
}
#endregion
}
#endregion
#endregion
protected override string GetDatabaseFieldNameForOrderBy(string orderBy)
{
//Some custom ones
@@ -1,37 +1,11 @@
using System.Collections;
using System.Collections.Generic;
using Umbraco.Core.Auditing;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IAuditRepository : IRepository<int, IAuditItem>
public interface IAuditRepository : IRepository<int, AuditItem>
{
/// <summary>
/// Return the audit items as paged result
/// </summary>
/// <param name="query">
/// The query coming from the service
/// </param>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="totalRecords"></param>
/// <param name="orderDirection"></param>
/// <param name="auditTypeFilter">
/// Since we currently do not have enum support with our expression parser, we cannot query on AuditType in the query or the custom filter
/// so we need to do that here
/// </param>
/// <param name="customFilter">
/// A user supplied custom filter
/// </param>
/// <returns></returns>
IEnumerable<IAuditItem> GetPagedResultsByQuery(
IQuery<IAuditItem> query,
long pageIndex, int pageSize, out long totalRecords,
Direction orderDirection,
AuditType[] auditTypeFilter,
IQuery<IAuditItem> customFilter);
}
}
@@ -9,8 +9,6 @@ namespace Umbraco.Core.Persistence.Repositories
{
public interface IMediaRepository : IRepositoryVersionable<int, IMedia>, IRecycleBinRepository<IMedia>, IReadRepository<Guid, IMedia>, IDeleteMediaFilesRepository
{
IMedia GetMediaByPath(string mediaPath);
/// <summary>
/// Used to add/update published xml for the media item
/// </summary>
@@ -91,12 +91,7 @@ namespace Umbraco.Core.Persistence.Repositories
IProfile GetProfile(string username);
IProfile GetProfile(int id);
IDictionary<UserState, int> GetUserStates();
Guid CreateLoginSession(int userId, string requestingIpAddress, bool cleanStaleSessions = true);
bool ValidateLoginSession(int userId, Guid sessionId);
int ClearLoginSessions(int userId);
int ClearLoginSessions(TimeSpan timespan);
void ClearLoginSession(Guid sessionId);
IDictionary<UserState, int> GetUserStates();
}
}
@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using System.Text;
using System.Xml.Linq;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
@@ -87,7 +87,7 @@ namespace Umbraco.Core.Persistence.Repositories
#region Overrides of PetaPocoRepositoryBase<int,IMedia>
private Sql GetBaseQuery(BaseQueryType queryType, bool includeFilePaths)
protected override Sql GetBaseQuery(BaseQueryType queryType)
{
var sql = new Sql();
sql.Select(queryType == BaseQueryType.Count ? "COUNT(*)" : (queryType == BaseQueryType.Ids ? "cmsContentVersion.contentId" : "*"))
@@ -95,26 +95,14 @@ namespace Umbraco.Core.Persistence.Repositories
.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 (includeFilePaths)
{
sql.InnerJoin<MediaDto>(SqlSyntax)
.On<MediaDto, ContentVersionDto>(SqlSyntax, left => left.VersionId, right => right.VersionId);
}
//TODO: IF we want to enable querying on content type information this will need to be joined
//.InnerJoin<ContentTypeDto>(SqlSyntax)
//.On<ContentDto, ContentTypeDto>(SqlSyntax, left => left.ContentTypeId, right => right.NodeId, SqlSyntax);
sql.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId, SqlSyntax);
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId, SqlSyntax)
//TODO: IF we want to enable querying on content type information this will need to be joined
//.InnerJoin<ContentTypeDto>(SqlSyntax)
//.On<ContentDto, ContentTypeDto>(SqlSyntax, left => left.ContentTypeId, right => right.NodeId, SqlSyntax);
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId, SqlSyntax);
return sql;
}
protected override Sql GetBaseQuery(BaseQueryType queryType)
{
return GetBaseQuery(queryType, false);
}
protected override Sql GetBaseQuery(bool isCount)
{
return GetBaseQuery(isCount ? BaseQueryType.Count : BaseQueryType.FullSingle);
@@ -140,7 +128,6 @@ namespace Umbraco.Core.Persistence.Repositories
"DELETE FROM cmsDocument WHERE nodeId = @Id",
"DELETE FROM cmsPropertyData WHERE contentNodeId = @Id",
"DELETE FROM cmsPreviewXml WHERE nodeId = @Id",
"DELETE FROM cmsMedia WHERE nodeId = @Id",
"DELETE FROM cmsContentVersion WHERE ContentId = @Id",
"DELETE FROM cmsContentXml WHERE nodeId = @Id",
"DELETE FROM cmsContent WHERE nodeId = @Id",
@@ -380,7 +367,7 @@ namespace Umbraco.Core.Persistence.Repositories
//Ensure that strings don't contain characters that are invalid in XML
entity.SanitizeEntityPropertiesForXmlStorage();
var factory = new MediaFactory(NodeObjectTypeId);
var factory = new MediaFactory(NodeObjectTypeId, entity.Id);
var dto = factory.BuildDto(entity);
//NOTE Should the logic below have some kind of fallback for empty parent ids ?
@@ -393,7 +380,7 @@ namespace Umbraco.Core.Persistence.Repositories
var sortOrder = maxSortOrder + 1;
//Create the (base) node data - umbracoNode
var nodeDto = dto.ContentVersionDto.ContentDto.NodeDto;
var nodeDto = dto.ContentDto.NodeDto;
nodeDto.Path = parent.Path;
nodeDto.Level = short.Parse(level.ToString(CultureInfo.InvariantCulture));
nodeDto.SortOrder = sortOrder;
@@ -428,21 +415,15 @@ namespace Umbraco.Core.Persistence.Repositories
entity.Level = level;
//Create the Content specific data - cmsContent
var contentDto = dto.ContentVersionDto.ContentDto;
var contentDto = dto.ContentDto;
contentDto.NodeId = nodeDto.NodeId;
Database.Insert(contentDto);
//Create the first version - cmsContentVersion
//Assumes a new Version guid and Version date (modified date) has been set
var contentVersionDto = dto.ContentVersionDto;
contentVersionDto.NodeId = nodeDto.NodeId;
Database.Insert(contentVersionDto);
//Create the Media specific data for this version - cmsMedia
//Assumes a new Version guid has been generated
dto.NodeId = nodeDto.NodeId;
Database.Insert(dto);
//Create the PropertyData for this version - cmsPropertyData
var propertyFactory = new PropertyFactory(entity.ContentType.CompositionPropertyTypes.ToArray(), entity.Version, entity.Id);
var propertyDataDtos = propertyFactory.BuildDto(entity.Properties);
@@ -490,14 +471,14 @@ namespace Umbraco.Core.Persistence.Repositories
entity.SortOrder = maxSortOrder + 1;
}
var factory = new MediaFactory(NodeObjectTypeId);
var factory = new MediaFactory(NodeObjectTypeId, entity.Id);
//Look up Content entry to get Primary for updating the DTO
var contentDto = Database.First<ContentDto>("WHERE nodeId = @Id", new { Id = entity.Id });
var contentDto = Database.SingleOrDefault<ContentDto>("WHERE nodeId = @Id", new { Id = entity.Id });
factory.SetPrimaryKey(contentDto.PrimaryKey);
var dto = factory.BuildDto(entity);
//Updates the (base) node data - umbracoNode
var nodeDto = dto.ContentVersionDto.ContentDto.NodeDto;
var nodeDto = dto.ContentDto.NodeDto;
nodeDto.ValidatePathWithException();
var o = Database.Update(nodeDto);
@@ -505,18 +486,15 @@ namespace Umbraco.Core.Persistence.Repositories
if (contentDto.ContentTypeId != entity.ContentTypeId)
{
//Create the Content specific data - cmsContent
var newContentDto = dto.ContentVersionDto.ContentDto;
var newContentDto = dto.ContentDto;
Database.Update(newContentDto);
}
//In order to update the ContentVersion we need to retrieve its primary key id
var contentVerDto = Database.First<ContentVersionDto>("WHERE VersionId = @Version", new { Version = entity.Version });
dto.ContentVersionDto.Id = contentVerDto.Id;
var contentVerDto = Database.SingleOrDefault<ContentVersionDto>("WHERE VersionId = @Version", new { Version = entity.Version });
dto.Id = contentVerDto.Id;
//Updates the current version - cmsContentVersion
//Assumes a Version guid exists and Version date (modified date) has been set/updated
Database.Update(dto.ContentVersionDto);
//now update the media entry
Database.Update(dto);
//Create the PropertyData for this version - cmsPropertyData
@@ -661,39 +639,7 @@ namespace Umbraco.Core.Persistence.Repositories
}
#endregion
}
#endregion
/// <summary>
/// Gets an <see cref="IMedia"/> object from the path stored in the for the media item.
/// </summary>
/// <param name="mediaPath">Path of the media item to retrieve (for example: /media/1024/koala_403x328.jpg)</param>
/// <returns><see cref="IMedia"/></returns>
public IMedia GetMediaByPath(string mediaPath)
{
var umbracoFileValue = mediaPath;
const string pattern = ".*[_][0-9]+[x][0-9]+[.].*";
var isResized = Regex.IsMatch(mediaPath, pattern);
// If the image has been resized we strip the "_403x328" of the original "/media/1024/koala_403x328.jpg" url.
if (isResized)
{
var underscoreIndex = mediaPath.LastIndexOf('_');
var dotIndex = mediaPath.LastIndexOf('.');
umbracoFileValue = string.Concat(mediaPath.Substring(0, underscoreIndex), mediaPath.Substring(dotIndex));
}
var sql = GetBaseQuery(BaseQueryType.FullSingle, true);
sql.Where<MediaDto>(mediaDto => mediaDto.MediaPath == umbracoFileValue, SqlSyntax);
sql.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
var dto = Database.Fetch<ContentVersionDto, ContentDto, NodeDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
if (dto == null)
return null;
var content = CreateMediaFromDto(dto, sql);
return content;
}
#endregion
/// <summary>
/// Gets paged media results
@@ -757,8 +703,8 @@ namespace Umbraco.Core.Persistence.Repositories
if (EnsureUniqueNaming == false)
return nodeName;
var names = Database.Fetch<SimilarNodeName>("SELECT id, text AS name FROM umbracoNode WHERE nodeObjectType=@objectType AND parentId=@parentId",
new { objectType = NodeObjectTypeId, parentId });
var names = Database.Fetch<SimilarNodeName>("SELECT id, text AS name FROM umbracoNode WHERE nodeObjectType=@objectType AND parentId=@parentId",
new { objectType = NodeObjectTypeId, parentId });
return SimilarNodeName.GetUniqueName(names, id, nodeName);
}
@@ -183,17 +183,17 @@ namespace Umbraco.Core.Persistence.Repositories
}
public IEnumerable<IMemberGroup> GetMemberGroupsForMember(string username)
{
var sql = new Sql()
{
var sql = new Sql()
.Select("un.*")
.From("umbracoNode AS un")
.InnerJoin("cmsMember2MemberGroup")
.On("un.id = cmsMember2MemberGroup.MemberGroup")
.LeftJoin("(SELECT umbracoNode.id, cmsMember.LoginName FROM umbracoNode INNER JOIN cmsMember ON umbracoNode.id = cmsMember.nodeId) AS member")
.On("member.id = cmsMember2MemberGroup.Member")
.Where("un.nodeObjectType=@objectType", new {objectType = NodeObjectTypeId })
.Where("member.LoginName=@loginName", new {loginName = username});
.On("cmsMember2MemberGroup.MemberGroup = un.id")
.InnerJoin("cmsMember")
.On("cmsMember.nodeId = cmsMember2MemberGroup.Member")
.Where("un.nodeObjectType=@objectType", new { objectType = NodeObjectTypeId })
.Where("cmsMember.LoginName=@loginName", new { loginName = username });
return Database.Fetch<NodeDto>(sql)
.DistinctBy(dto => dto.NodeId)
.Select(x => _modelFactory.BuildEntity(x));
@@ -1,9 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Persistence.UnitOfWork;
@@ -48,16 +53,15 @@ namespace Umbraco.Core.Persistence.Repositories
@"DELETE FROM umbracoRedirectUrl WHERE umbracoRedirectUrl.id IN(
SELECT TB1.id FROM umbracoRedirectUrl as TB1
INNER JOIN umbracoNode as TB2 ON TB1.contentKey = TB2.uniqueId
WHERE TB2.trashed = '1' AND TB2.nodeObjectType = @NodeObjectType)",
WHERE TB2.trashed = '1' AND TB2.nodeObjectType = @NodeObjectType)",
FormatDeleteStatement("umbracoUserStartNode", "startNode"),
FormatUpdateStatement("umbracoUserGroup", "startContentId"),
FormatUpdateStatement("umbracoUserGroup", "startMediaId"),
FormatUpdateStatement("umbracoUserGroup", "startMediaId"),
FormatDeleteStatement("umbracoRelation", "parentId"),
FormatDeleteStatement("umbracoRelation", "childId"),
FormatDeleteStatement("cmsTagRelationship", "nodeId"),
FormatDeleteStatement("umbracoDomains", "domainRootStructureID"),
FormatDeleteStatement("cmsDocument", "nodeId"),
FormatDeleteStatement("cmsMedia", "nodeId"),
FormatDeleteStatement("cmsPropertyData", "contentNodeId"),
FormatDeleteStatement("cmsPreviewXml", "nodeId"),
FormatDeleteStatement("cmsContentVersion", "ContentId"),
@@ -4,6 +4,7 @@ using System.Linq;
using Umbraco.Core.Cache;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Scoping;
@@ -20,6 +20,12 @@ namespace Umbraco.Core.Persistence.Repositories
var name = Name;
// cater for instances where node has no name.
if(string.IsNullOrWhiteSpace(name))
{
return _numPos;
}
if (name[name.Length - 1] != ')')
return _numPos = -1;
@@ -106,7 +112,7 @@ namespace Umbraco.Core.Persistence.Repositories
}
}
return uniqueing ? string.Concat(nodeName, " (", uniqueNumber.ToString(), ")") : nodeName;
return uniqueing || string.IsNullOrWhiteSpace(nodeName) ? string.Concat(nodeName, " (", uniqueNumber.ToString(), ")") : nodeName;
}
}
}
@@ -6,7 +6,6 @@ using System.Linq.Expressions;
using System.Text;
using System.Web.Security;
using Newtonsoft.Json;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Membership;
@@ -188,95 +187,18 @@ UNION
SELECT '5CountOfInvited' AS colName, COUNT(id) AS num FROM umbracoUser WHERE lastLoginDate IS NULL AND userDisabled = 1 AND invitedDate IS NOT NULL
ORDER BY colName";
var result = Database.Fetch<dynamic>(sql);
var result = Database.Fetch<dynamic>(sql);
return new Dictionary<UserState, int>
{
{UserState.All, result[0].num},
{UserState.Active, result[1].num},
{UserState.Disabled, result[2].num},
{UserState.LockedOut, result[3].num},
{UserState.Invited, result[4].num}
{UserState.All, (int)result[0].num},
{UserState.Active, (int)result[1].num},
{UserState.Disabled, (int)result[2].num},
{UserState.LockedOut, (int)result[3].num},
{UserState.Invited, (int)result[4].num}
};
}
public Guid CreateLoginSession(int userId, string requestingIpAddress, bool cleanStaleSessions = true)
{
//TODO: I know this doesn't follow the normal repository conventions which would require us to crete a UserSessionRepository
//and also business logic models for these objects but that's just so overkill for what we are doing
//and now that everything is properly in a transaction (Scope) there doesn't seem to be much reason for using that anymore
var now = DateTime.UtcNow;
var dto = new UserLoginDto
{
UserId = userId,
IpAddress = requestingIpAddress,
LoggedInUtc = now,
LastValidatedUtc = now,
LoggedOutUtc = null,
SessionId = Guid.NewGuid()
};
Database.Insert(dto);
if (cleanStaleSessions)
{
ClearLoginSessions(TimeSpan.FromDays(15));
}
return dto.SessionId;
}
public bool ValidateLoginSession(int userId, Guid sessionId)
{
var found = Database.FirstOrDefault<UserLoginDto>("WHERE sessionId=@sessionId", new {sessionId = sessionId});
if (found == null || found.UserId != userId || found.LoggedOutUtc.HasValue)
return false;
//now detect if there's been a timeout
if (DateTime.UtcNow - found.LastValidatedUtc > TimeSpan.FromMinutes(GlobalSettings.TimeOutInMinutes))
{
//timeout detected, update the record
ClearLoginSession(sessionId);
return false;
}
//update the validate date
found.LastValidatedUtc = DateTime.UtcNow;
Database.Update(found);
return true;
}
public int ClearLoginSessions(int userId)
{
//TODO: I know this doesn't follow the normal repository conventions which would require us to crete a UserSessionRepository
//and also business logic models for these objects but that's just so overkill for what we are doing
//and now that everything is properly in a transaction (Scope) there doesn't seem to be much reason for using that anymore
var count = Database.ExecuteScalar<int>("SELECT COUNT(*) FROM umbracoUserLogin WHERE userId=@userId", new { userId = userId });
Database.Execute("DELETE FROM umbracoUserLogin WHERE userId=@userId", new {userId = userId});
return count;
}
public int ClearLoginSessions(TimeSpan timespan)
{
//TODO: I know this doesn't follow the normal repository conventions which would require us to crete a UserSessionRepository
//and also business logic models for these objects but that's just so overkill for what we are doing
//and now that everything is properly in a transaction (Scope) there doesn't seem to be much reason for using that anymore
var fromDate = DateTime.UtcNow - timespan;
var count = Database.ExecuteScalar<int>("SELECT COUNT(*) FROM umbracoUserLogin WHERE lastValidatedUtc=@fromDate", new { fromDate = fromDate });
Database.Execute("DELETE FROM umbracoUserLogin WHERE lastValidatedUtc=@fromDate", new { fromDate = fromDate });
return count;
}
public void ClearLoginSession(Guid sessionId)
{
//TODO: I know this doesn't follow the normal repository conventions which would require us to crete a UserSessionRepository
//and also business logic models for these objects but that's just so overkill for what we are doing
//and now that everything is properly in a transaction (Scope) there doesn't seem to be much reason for using that anymore
Database.Execute("UPDATE umbracoUserLogin SET loggedOutUtc=@now WHERE sessionId=@sessionId",
new { now = DateTime.UtcNow, sessionId = sessionId });
}
protected override IEnumerable<IUser> PerformGetAll(params int[] ids)
{
var sql = GetQueryWithGroups();
@@ -479,8 +401,7 @@ ORDER BY colName";
{"updateDate", "UpdateDate"},
{"avatar", "Avatar"},
{"emailConfirmedDate", "EmailConfirmedDate"},
{"invitedDate", "InvitedDate"},
{"tourData", "TourData"}
{"invitedDate", "InvitedDate"}
};
//create list of properties that have changed
@@ -913,6 +834,14 @@ ORDER BY colName";
{
return dtos.Select(UserFactory.BuildEntity);
}
//private IEnumerable<IUserGroup> ConvertFromDtos(IEnumerable<UserGroupDto> dtos)
//{
// return dtos.Select(dto =>
// {
// var userGroupFactory = new UserGroupFactory();
// return userGroupFactory.BuildEntity(dto);
// });
//}
}
}
@@ -18,6 +18,8 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
return Constants.PropertyEditors.MultipleTextstringAlias.Equals(propertyType.PropertyEditorAlias);
}
private static readonly string[] NewLineDelimiters = { "\r\n", "\r", "\n" };
public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
{
// data is (both in database and xml):
@@ -53,7 +55,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
// Fall back on normal behaviour
if (values.Any() == false)
{
return sourceString.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
return sourceString.Split(NewLineDelimiters, StringSplitOptions.None);
}
return values.ToArray();
@@ -11,14 +11,12 @@ using System.Security.Principal;
using System.Threading;
using System.Web;
using System.Web.Security;
using Microsoft.AspNet.Identity;
using AutoMapper;
using Microsoft.Owin;
using Newtonsoft.Json;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Logging;
using IUser = Umbraco.Core.Models.Membership.IUser;
namespace Umbraco.Core.Security
{
@@ -243,11 +241,8 @@ namespace Umbraco.Core.Security
/// <param name="userdata"></param>
public static FormsAuthenticationTicket CreateUmbracoAuthTicket(this HttpContextBase http, UserData userdata)
{
//ONLY used by BasePage.doLogin!
if (http == null) throw new ArgumentNullException("http");
if (userdata == null) throw new ArgumentNullException("userdata");
if (userdata == null) throw new ArgumentNullException("userdata");
var userDataString = JsonConvert.SerializeObject(userdata);
return CreateAuthTicketAndCookie(
http,
@@ -259,7 +254,14 @@ namespace Umbraco.Core.Security
1440,
UmbracoConfig.For.UmbracoSettings().Security.AuthCookieName,
UmbracoConfig.For.UmbracoSettings().Security.AuthCookieDomain);
}
}
internal static FormsAuthenticationTicket CreateUmbracoAuthTicket(this HttpContext http, UserData userdata)
{
if (http == null) throw new ArgumentNullException("http");
if (userdata == null) throw new ArgumentNullException("userdata");
return new HttpContextWrapper(http).CreateUmbracoAuthTicket(userdata);
}
/// <summary>
/// returns the number of seconds the user has until their auth session times out
@@ -329,23 +331,7 @@ namespace Umbraco.Core.Security
/// <param name="http"></param>
/// <param name="cookieName"></param>
private static void Logout(this HttpContextBase http, string cookieName)
{
//We need to clear the sessionId from the database. This is legacy code to do any logging out and shouldn't really be used at all but in any case
//we need to make sure the session is cleared. Due to the legacy nature of this it means we need to use singletons
if (http.User != null)
{
var claimsIdentity = http.User.Identity as ClaimsIdentity;
if (claimsIdentity != null)
{
var sessionId = claimsIdentity.FindFirstValue(Constants.Security.SessionIdClaimType);
Guid guidSession;
if (sessionId.IsNullOrWhiteSpace() == false && Guid.TryParse(sessionId, out guidSession))
{
ApplicationContext.Current.Services.UserService.ClearLoginSession(guidSession);
}
}
}
{
if (http == null) throw new ArgumentNullException("http");
//clear the preview cookie and external login
var cookies = new[] { cookieName, Constants.Web.PreviewCookieName, Constants.Security.BackOfficeExternalCookieName };
@@ -2,29 +2,16 @@
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNet.Identity;
using Umbraco.Core.Models.Identity;
using Umbraco.Core.Services;
namespace Umbraco.Core.Security
{
public class BackOfficeClaimsIdentityFactory<T> : ClaimsIdentityFactory<T, int>
where T: BackOfficeIdentityUser
{
private readonly ApplicationContext _appCtx;
[Obsolete("Use the overload specifying all dependencies instead")]
public BackOfficeClaimsIdentityFactory()
:this(ApplicationContext.Current)
{
}
public BackOfficeClaimsIdentityFactory(ApplicationContext appCtx)
{
if (appCtx == null) throw new ArgumentNullException("appCtx");
_appCtx = appCtx;
SecurityStampClaimType = Constants.Security.SessionIdClaimType;
UserNameClaimType = ClaimTypes.Name;
}
@@ -37,9 +24,9 @@ namespace Umbraco.Core.Security
public override async Task<ClaimsIdentity> CreateAsync(UserManager<T, int> manager, T user, string authenticationType)
{
var baseIdentity = await base.CreateAsync(manager, user, authenticationType);
var umbracoIdentity = new UmbracoBackOfficeIdentity(baseIdentity,
//NOTE - there is no session id assigned here, this is just creating the identity, a session id will be generated when the cookie is written
//set a new session id
new UserData
{
Id = user.Id,
@@ -50,22 +37,15 @@ namespace Umbraco.Core.Security
Roles = user.Roles.Select(x => x.RoleId).ToArray(),
StartContentNodes = user.CalculatedContentStartNodeIds,
StartMediaNodes = user.CalculatedMediaStartNodeIds,
SecurityStamp = user.SecurityStamp
SessionId = user.SecurityStamp
});
return umbracoIdentity;
}
}
}
public class BackOfficeClaimsIdentityFactory : BackOfficeClaimsIdentityFactory<BackOfficeIdentityUser>
{
[Obsolete("Use the overload specifying all dependencies instead")]
public BackOfficeClaimsIdentityFactory()
{
}
{
public BackOfficeClaimsIdentityFactory(ApplicationContext appCtx) : base(appCtx)
{
}
}
}
@@ -1,83 +1,21 @@
using System;
using System.Collections.Concurrent;
using System.ComponentModel;
using System.Globalization;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Semver;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models.Identity;
namespace Umbraco.Core.Security
{
public class BackOfficeCookieAuthenticationProvider : CookieAuthenticationProvider
{
private readonly ApplicationContext _appCtx;
[Obsolete("Use the ctor specifying all dependencies")]
[EditorBrowsable(EditorBrowsableState.Never)]
public BackOfficeCookieAuthenticationProvider()
: this(ApplicationContext.Current)
{
}
public BackOfficeCookieAuthenticationProvider(ApplicationContext appCtx)
{
if (appCtx == null) throw new ArgumentNullException("appCtx");
_appCtx = appCtx;
}
private static readonly SemVersion MinUmbracoVersionSupportingLoginSessions = new SemVersion(7, 8);
public override void ResponseSignIn(CookieResponseSignInContext context)
{
var backOfficeIdentity = context.Identity as UmbracoBackOfficeIdentity;
if (backOfficeIdentity != null)
{
//generate a session id and assign it
//create a session token - if we are configured and not in an upgrade state then use the db, otherwise just generate one
//NOTE - special check because when we are upgrading to 7.8 we cannot create a session since the db isn't ready and we'll get exceptions
var canAcquireSession = _appCtx.IsUpgrading == false || _appCtx.CurrentVersion() >= MinUmbracoVersionSupportingLoginSessions;
var session = canAcquireSession
? _appCtx.Services.UserService.CreateLoginSession((int)backOfficeIdentity.Id, context.OwinContext.GetCurrentRequestIpAddress())
: Guid.NewGuid();
backOfficeIdentity.UserData.SessionId = session.ToString();
}
base.ResponseSignIn(context);
}
public override void ResponseSignOut(CookieResponseSignOutContext context)
{
//Clear the user's session on sign out
if (context != null && context.OwinContext != null && context.OwinContext.Authentication != null
&& context.OwinContext.Authentication.User != null && context.OwinContext.Authentication.User.Identity != null)
{
var claimsIdentity = context.OwinContext.Authentication.User.Identity as ClaimsIdentity;
var sessionId = claimsIdentity.FindFirstValue(Constants.Security.SessionIdClaimType);
Guid guidSession;
if (sessionId.IsNullOrWhiteSpace() == false && Guid.TryParse(sessionId, out guidSession))
{
_appCtx.Services.UserService.ClearLoginSession(guidSession);
}
}
base.ResponseSignOut(context);
//Make sure the definitely all of these cookies are cleared when signing out with cookies
context.Response.Cookies.Append(SessionIdValidator.CookieName, "", new CookieOptions
{
Expires = DateTime.Now.AddYears(-1),
Path = "/"
});
context.Response.Cookies.Append(UmbracoConfig.For.UmbracoSettings().Security.AuthCookieName, "", new CookieOptions
{
Expires = DateTime.Now.AddYears(-1),
@@ -96,45 +34,21 @@ namespace Umbraco.Core.Security
}
/// <summary>
/// Ensures that the culture is set correctly for the current back office user and that the user's session token is valid
/// Ensures that the culture is set correctly for the current back office user
/// </summary>
/// <param name="context"/>
/// <returns/>
public override async Task ValidateIdentity(CookieValidateIdentityContext context)
{
EnsureCulture(context);
await EnsureValidSessionId(context);
await base.ValidateIdentity(context);
}
/// <summary>
/// Ensures that the user has a valid session id
/// </summary>
/// <remarks>
/// So that we are not overloading the database this throttles it's check to every minute
/// </remarks>
protected virtual async Task EnsureValidSessionId(CookieValidateIdentityContext context)
{
if (_appCtx.IsConfigured && _appCtx.IsUpgrading == false)
await SessionIdValidator.ValidateSessionAsync(TimeSpan.FromMinutes(1), context);
}
private void EnsureCulture(CookieValidateIdentityContext context)
public override Task ValidateIdentity(CookieValidateIdentityContext context)
{
var umbIdentity = context.Identity as UmbracoBackOfficeIdentity;
if (umbIdentity != null && umbIdentity.IsAuthenticated)
{
Thread.CurrentThread.CurrentCulture =
Thread.CurrentThread.CurrentUICulture =
UserCultures.GetOrAdd(umbIdentity.Culture, s => new CultureInfo(s));
new CultureInfo(umbIdentity.Culture);
}
}
/// <summary>
/// Used so that we aren't creating a new CultureInfo object for every single request
/// </summary>
private static readonly ConcurrentDictionary<string, CultureInfo> UserCultures = new ConcurrentDictionary<string, CultureInfo>();
return base.ValidateIdentity(context);
}
}
}
@@ -223,9 +223,6 @@ namespace Umbraco.Core.Security
user.AccessFailedCount = 0;
await UserManager.UpdateAsync(user);
//set the current request's principal to the identity just signed in!
_request.User = new ClaimsPrincipal(userIdentity);
_logger.WriteCore(TraceEventType.Information, 0,
string.Format(
"Login attempt succeeded for username {0} from IP address {1}",
@@ -152,6 +152,7 @@ namespace Umbraco.Core.Security
//NOTE: This method is mostly here for backwards compat
base.InitUserManager(manager, membershipProvider, options.DataProtectionProvider, contentSectionConfig);
}
}
/// <summary>
@@ -168,7 +169,7 @@ namespace Umbraco.Core.Security
#region What we support do not currently
//TODO: We could support this - but a user claims will mostly just be what is in the auth cookie
//NOTE: Not sure if we really want/need to ever support this
public override bool SupportsUserClaim
{
get { return false; }
@@ -270,22 +271,6 @@ namespace Umbraco.Core.Security
//manager.SmsService = new SmsService();
}
/// <summary>
/// Used to validate a user's session
/// </summary>
/// <param name="userId"></param>
/// <param name="sessionId"></param>
/// <returns></returns>
public virtual async Task<bool> ValidateSessionIdAsync(int userId, string sessionId)
{
var userSessionStore = Store as IUserSessionStore<BackOfficeIdentityUser, int>;
//if this is not set, for backwards compat (which would be super rare), we'll just approve it
if (userSessionStore == null)
return true;
return await userSessionStore.ValidateSessionIdAsync(userId, sessionId);
}
/// <summary>
/// This will determine which password hasher to use based on what is defined in config
/// </summary>
@@ -689,5 +674,4 @@ namespace Umbraco.Core.Security
return httpContext.GetCurrentRequestIpAddress();
}
}
}
@@ -26,13 +26,12 @@ namespace Umbraco.Core.Security
IUserRoleStore<BackOfficeIdentityUser, int>,
IUserSecurityStampStore<BackOfficeIdentityUser, int>,
IUserLockoutStore<BackOfficeIdentityUser, int>,
IUserTwoFactorStore<BackOfficeIdentityUser, int>,
IUserSessionStore<BackOfficeIdentityUser, int>
IUserTwoFactorStore<BackOfficeIdentityUser, int>
//TODO: This would require additional columns/tables for now people will need to implement this on their own
//IUserPhoneNumberStore<BackOfficeIdentityUser, int>,
//TODO: To do this we need to implement IQueryable - we'll have an IQuerable implementation soon with the UmbracoLinqPadDriver implementation
//IQueryableUserStore<BackOfficeIdentityUser, int>
//TODO: This would require additional columns/tables for now people will need to implement this on their own
//IUserPhoneNumberStore<BackOfficeIdentityUser, int>,
//TODO: To do this we need to implement IQueryable - we'll have an IQuerable implementation soon with the UmbracoLinqPadDriver implementation
//IQueryableUserStore<BackOfficeIdentityUser, int>
{
private readonly IUserService _userService;
private readonly IEntityService _entityService;
@@ -754,15 +753,6 @@ namespace Umbraco.Core.Security
if (_disposed)
throw new ObjectDisposedException(GetType().Name);
}
public Task<bool> ValidateSessionIdAsync(int userId, string sessionId)
{
Guid guidSessionId;
if (Guid.TryParse(sessionId, out guidSessionId))
{
return Task.FromResult(_userService.ValidateLoginSession(userId, guidSessionId));
}
return Task.FromResult(false);
}
}
}
@@ -1,17 +0,0 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
namespace Umbraco.Core.Security
{
/// <summary>
/// An IUserStore interface part to implement if the store supports validating user session Ids
/// </summary>
/// <typeparam name="TUser"></typeparam>
/// <typeparam name="TKey"></typeparam>
public interface IUserSessionStore<TUser, in TKey> : IUserStore<TUser, TKey>, IDisposable
where TUser : class, IUser<TKey>
{
Task<bool> ValidateSessionIdAsync(int userId, string sessionId);
}
}
@@ -1,5 +1,4 @@
using System;
using System.Web;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using Umbraco.Core.Models.Identity;
@@ -8,37 +7,6 @@ namespace Umbraco.Core.Security
{
public static class OwinExtensions
{
public static string GetCurrentRequestIpAddress(this IOwinContext owinContext)
{
if (owinContext == null)
{
return "Unknown, owinContext is null";
}
if (owinContext.Request == null)
{
return "Unknown, owinContext.Request is null";
}
var httpContext = owinContext.TryGetHttpContext();
if (httpContext == false)
{
return "Unknown, cannot resolve HttpContext from owinContext";
}
return httpContext.Result.GetCurrentRequestIpAddress();
}
/// <summary>
/// Nasty little hack to get httpcontextbase from an owin context
/// </summary>
/// <param name="owinContext"></param>
/// <returns></returns>
internal static Attempt<HttpContextBase> TryGetHttpContext(this IOwinContext owinContext)
{
var ctx = owinContext.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
return ctx == null ? Attempt<HttpContextBase>.Fail() : Attempt.Succeed(ctx);
}
/// <summary>
/// Gets the back office sign in manager out of OWIN
/// </summary>
@@ -1,118 +0,0 @@
using System;
using System.Collections.Concurrent;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using Microsoft.Owin.Infrastructure;
using Microsoft.Owin.Security.Cookies;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models.Identity;
namespace Umbraco.Core.Security
{
/// <summary>
/// Static helper class used to configure a CookieAuthenticationProvider to validate a cookie against a user's session id
/// </summary>
/// <remarks>
/// This uses another cookie to track the last checked time which is done for a few reasons:
/// * We can't use the user's auth ticket to do thsi because we'd be re-issuing the auth ticket all of the time and it would never expire
/// plus the auth ticket size is much larger than this small value
/// * This will execute quite often (every minute per user) and in some cases there might be several requests that end up re-issuing the cookie so the cookie value should be small
/// * We want to avoid the user lookup if it's not required so that will only happen when the time diff is great enough in the cookie
/// </remarks>
internal static class SessionIdValidator
{
public const string CookieName = "UMB_UCONTEXT_C";
public static async Task ValidateSessionAsync(TimeSpan validateInterval, CookieValidateIdentityContext context)
{
if (context.Request.Uri.IsBackOfficeRequest(HttpRuntime.AppDomainAppVirtualPath) == false)
return;
var valid = await ValidateSessionAsync(validateInterval, context.OwinContext, context.Options.CookieManager, context.Options.SystemClock, context.Properties.IssuedUtc, context.Identity);
if (valid == false)
{
context.RejectIdentity();
context.OwinContext.Authentication.SignOut(context.Options.AuthenticationType);
}
}
public static async Task<bool> ValidateSessionAsync(
TimeSpan validateInterval,
IOwinContext owinCtx,
ICookieManager cookieManager,
ISystemClock systemClock,
DateTimeOffset? authTicketIssueDate,
ClaimsIdentity currentIdentity)
{
if (owinCtx == null) throw new ArgumentNullException("owinCtx");
if (cookieManager == null) throw new ArgumentNullException("cookieManager");
if (systemClock == null) throw new ArgumentNullException("systemClock");
DateTimeOffset? issuedUtc = null;
var currentUtc = systemClock.UtcNow;
//read the last checked time from a custom cookie
var lastCheckedCookie = cookieManager.GetRequestCookie(owinCtx, CookieName);
if (lastCheckedCookie.IsNullOrWhiteSpace() == false)
{
DateTimeOffset parsed;
if (DateTimeOffset.TryParse(lastCheckedCookie, out parsed))
{
issuedUtc = parsed;
}
}
//no cookie, use the issue time of the auth ticket
if (issuedUtc.HasValue == false)
{
issuedUtc = authTicketIssueDate;
}
// Only validate if enough time has elapsed
var validate = issuedUtc.HasValue == false;
if (issuedUtc.HasValue)
{
var timeElapsed = currentUtc.Subtract(issuedUtc.Value);
validate = timeElapsed > validateInterval;
}
if (validate == false)
return true;
var manager = owinCtx.GetUserManager<BackOfficeUserManager>();
if (manager == null)
return false;
var userId = currentIdentity.GetUserId<int>();
var user = await manager.FindByIdAsync(userId);
if (user == null)
return false;
var sessionId = currentIdentity.FindFirstValue(Constants.Security.SessionIdClaimType);
if (await manager.ValidateSessionIdAsync(userId, sessionId) == false)
return false;
//we will re-issue the cookie last checked cookie
cookieManager.AppendResponseCookie(
owinCtx,
CookieName,
DateTimeOffset.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffffffzzz"),
new CookieOptions
{
HttpOnly = true,
Secure = GlobalSettings.UseSSL || owinCtx.Request.IsSecure,
Path = "/"
});
return true;
}
}
}
@@ -36,7 +36,6 @@ namespace Umbraco.Core.Security
var username = identity.GetUserName();
var session = identity.FindFirstValue(Constants.Security.SessionIdClaimType);
var securityStamp = identity.FindFirstValue(Microsoft.AspNet.Identity.Constants.DefaultSecurityStampClaimType);
var startContentId = identity.FindFirstValue(Constants.Security.StartContentNodeIdClaimType);
var startMediaId = identity.FindFirstValue(Constants.Security.StartMediaNodeIdClaimType);
@@ -67,9 +66,8 @@ namespace Umbraco.Core.Security
var roles = identity.FindAll(x => x.Type == DefaultRoleClaimType).Select(role => role.Value).ToList();
var allowedApps = identity.FindAll(x => x.Type == Constants.Security.AllowedApplicationsClaimType).Select(app => app.Value).ToList();
var userData = new UserData
{
SecurityStamp = securityStamp,
var userData = new UserData(session)
{
SessionId = session,
AllowedApplications = allowedApps.ToArray(),
Culture = culture,
@@ -191,8 +189,7 @@ namespace Umbraco.Core.Security
Constants.Security.StartContentNodeIdClaimType,
Constants.Security.StartMediaNodeIdClaimType,
ClaimTypes.Locality,
Constants.Security.SessionIdClaimType,
Microsoft.AspNet.Identity.Constants.DefaultSecurityStampClaimType
Constants.Security.SessionIdClaimType
};
}
}
@@ -229,7 +226,7 @@ namespace Umbraco.Core.Security
// by the SecurityStampValidator, see: https://katanaproject.codeplex.com/workitem/444
if (HasClaim(x => x.Type == Microsoft.AspNet.Identity.Constants.DefaultSecurityStampClaimType) == false)
{
AddClaim(new Claim(Microsoft.AspNet.Identity.Constants.DefaultSecurityStampClaimType, SecurityStamp, ClaimValueTypes.String, Issuer, Issuer, this));
AddClaim(new Claim(Microsoft.AspNet.Identity.Constants.DefaultSecurityStampClaimType, SessionId, ClaimValueTypes.String, Issuer, Issuer, this));
}
}
@@ -310,11 +307,6 @@ namespace Umbraco.Core.Security
get { return UserData.SessionId; }
}
public string SecurityStamp
{
get { return UserData.SecurityStamp; }
}
public string[] Roles
{
get { return UserData.Roles; }
+4 -10
View File
@@ -20,7 +20,7 @@ namespace Umbraco.Core.Security
/// Use this constructor to create/assign new UserData to the ticket
/// </summary>
/// <param name="sessionId">
/// The current sessionId for the user
/// The security stamp for the user
/// </param>
public UserData(string sessionId)
{
@@ -29,18 +29,12 @@ namespace Umbraco.Core.Security
Roles = new string[] { };
}
/// <summary>
/// This is the 'sessionId' for validation
/// </summary>
[DataMember(Name = "sessionId")]
public string SessionId { get; set; }
/// <summary>
/// This is the 'security stamp' for validation
/// </summary>
[DataMember(Name = "securityStamp")]
public string SecurityStamp { get; set; }
[DataMember(Name = "sessionId")]
public string SessionId { get; set; }
[DataMember(Name = "id")]
public object Id { get; set; }
-85
View File
@@ -1,12 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Events;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Services
@@ -26,86 +22,5 @@ namespace Umbraco.Core.Services
uow.Commit();
}
}
/// <summary>
/// Returns paged items in the audit trail for a given entity
/// </summary>
/// <param name="entityId"></param>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="totalRecords"></param>
/// <param name="orderDirection">
/// By default this will always be ordered descending (newest first)
/// </param>
/// <param name="auditTypeFilter">
/// Since we currently do not have enum support with our expression parser, we cannot query on AuditType in the query or the custom filter
/// so we need to do that here
/// </param>
/// <param name="customFilter">
/// Optional filter to be applied
/// </param>
/// <returns></returns>
public IEnumerable<IAuditItem> GetPagedItemsByEntity(int entityId, long pageIndex, int pageSize, out long totalRecords,
Direction orderDirection = Direction.Descending,
AuditType[] auditTypeFilter = null,
IQuery<IAuditItem> customFilter = null)
{
Mandate.ParameterCondition(pageIndex >= 0, "pageIndex");
Mandate.ParameterCondition(pageSize > 0, "pageSize");
if (entityId == Constants.System.Root || entityId <= 0)
{
totalRecords = 0;
return Enumerable.Empty<IAuditItem>();
}
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
{
var repository = RepositoryFactory.CreateAuditRepository(uow);
var query = Query<IAuditItem>.Builder.Where(x => x.Id == entityId);
return repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, orderDirection, auditTypeFilter, customFilter);
}
}
/// <summary>
/// Returns paged items in the audit trail for a given user
/// </summary>
/// <param name="userId"></param>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="totalRecords"></param>
/// <param name="orderDirection">
/// By default this will always be ordered descending (newest first)
/// </param>
/// <param name="auditTypeFilter">
/// Since we currently do not have enum support with our expression parser, we cannot query on AuditType in the query or the custom filter
/// so we need to do that here
/// </param>
/// <param name="customFilter">
/// Optional filter to be applied
/// </param>
/// <returns></returns>
public IEnumerable<IAuditItem> GetPagedItemsByUser(int userId, long pageIndex, int pageSize, out long totalRecords, Direction orderDirection = Direction.Descending, AuditType[] auditTypeFilter = null, IQuery<IAuditItem> customFilter = null)
{
Mandate.ParameterCondition(pageIndex >= 0, "pageIndex");
Mandate.ParameterCondition(pageSize > 0, "pageSize");
if (userId < 0)
{
totalRecords = 0;
return Enumerable.Empty<IAuditItem>();
}
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
{
var repository = RepositoryFactory.CreateAuditRepository(uow);
var query = Query<IAuditItem>.Builder.Where(x => x.UserId == userId);
return repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, orderDirection, auditTypeFilter, customFilter);
}
}
}
}
+93 -3
View File
@@ -993,8 +993,7 @@ namespace Umbraco.Core.Services
FROM umbracoNode
JOIN cmsDocument ON umbracoNode.id=cmsDocument.nodeId AND cmsDocument.published=@0
WHERE umbracoNode.trashed=@1 AND umbracoNode.id IN (@2)",
true, false, ids);
Console.WriteLine(sql.SQL);
true, false, ids);
var x = uow.Database.Fetch<int>(sql);
return ids.Length == x.Count;
}
@@ -2009,6 +2008,96 @@ namespace Umbraco.Core.Services
return true;
}
/// <summary>
/// Sorts a collection of <see cref="IContent"/> objects by updating the SortOrder according
/// to the ordering of node Ids passed in.
/// </summary>
/// <remarks>
/// Using this method will ensure that the Published-state is maintained upon sorting
/// so the cache is updated accordingly - as needed.
/// </remarks>
/// <param name="ids"></param>
/// <param name="userId"></param>
/// <param name="raiseEvents"></param>
/// <returns>True if sorting succeeded, otherwise False</returns>
public bool Sort(int[] ids, int userId = 0, bool raiseEvents = true)
{
var shouldBePublished = new List<IContent>();
var shouldBeSaved = new List<IContent>();
using (new WriteLock(Locker))
{
var allContent = GetByIds(ids).ToDictionary(x => x.Id, x => x);
var items = ids.Select(x => allContent[x]);
using (var uow = UowProvider.GetUnitOfWork())
{
var asArray = items.ToArray();
var saveEventArgs = new SaveEventArgs<IContent>(asArray);
if (raiseEvents && uow.Events.DispatchCancelable(Saving, this, saveEventArgs, "Saving"))
{
uow.Commit();
return false;
}
var repository = RepositoryFactory.CreateContentRepository(uow);
var i = 0;
foreach (var content in asArray)
{
//If the current sort order equals that of the content
//we don't need to update it, so just increment the sort order
//and continue.
if (content.SortOrder == i)
{
i++;
continue;
}
content.SortOrder = i;
content.WriterId = userId;
i++;
if (content.Published)
{
//TODO: This should not be an inner operation, but if we do this, it cannot raise events and cannot be cancellable!
var published = _publishingStrategy.Publish(uow, content, userId).Success;
shouldBePublished.Add(content);
}
else
shouldBeSaved.Add(content);
repository.AddOrUpdate(content);
//add or update a preview
repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, _userService, c));
}
foreach (var content in shouldBePublished)
{
//Create and Save ContentXml DTO
repository.AddOrUpdateContentXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, _userService, c));
}
if (raiseEvents)
{
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs, "Saved");
}
if (shouldBePublished.Any())
{
//TODO: This should not be an inner operation, but if we do this, it cannot raise events and cannot be cancellable!
_publishingStrategy.PublishingFinalized(uow, shouldBePublished, false);
}
Audit(uow, AuditType.Sort, "Sorting content performed by user", userId, 0);
uow.Commit();
}
}
return true;
}
public IEnumerable<IContent> GetBlueprintsForContentTypes(params int[] documentTypeIds)
{
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
@@ -2453,6 +2542,7 @@ namespace Umbraco.Core.Services
Audit(uow, AuditType.Publish, "Save and Publish performed by user", userId, content.Id);
uow.Commit();
return Attempt.If(publishStatus.StatusType == PublishStatusType.Success, publishStatus);
}
}
@@ -2748,4 +2838,4 @@ namespace Umbraco.Core.Services
#endregion
}
}
}
@@ -377,7 +377,7 @@ namespace Umbraco.Core.Services
repo.Delete(container);
uow.Commit();
deleteEventArgs.CanCancel = false;
uow.Events.Dispatch(DeletedMediaTypeContainer, this, deleteEventArgs);
uow.Events.Dispatch(DeletedMediaTypeContainer, this, deleteEventArgs, "DeletedMediaTypeContainer");
return OperationStatus.Success(evtMsgs);
//TODO: Audit trail ?
@@ -1,58 +1,9 @@
using System.Collections.Generic;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Services
{
public interface IAuditService : IService
{
void Add(AuditType type, string comment, int userId, int objectId);
/// <summary>
/// Returns paged items in the audit trail for a given entity
/// </summary>
/// <param name="entityId"></param>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="totalRecords"></param>
/// <param name="orderDirection">
/// By default this will always be ordered descending (newest first)
/// </param>
/// <param name="auditTypeFilter">
/// Since we currently do not have enum support with our expression parser, we cannot query on AuditType in the query or the custom filter
/// so we need to do that here
/// </param>
/// <param name="customFilter">
/// Optional filter to be applied
/// </param>
/// <returns></returns>
IEnumerable<IAuditItem> GetPagedItemsByEntity(int entityId, long pageIndex, int pageSize, out long totalRecords,
Direction orderDirection = Direction.Descending,
AuditType[] auditTypeFilter = null,
IQuery<IAuditItem> customFilter = null);
/// <summary>
/// Returns paged items in the audit trail for a given user
/// </summary>
/// <param name="userId"></param>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="totalRecords"></param>
/// <param name="orderDirection">
/// By default this will always be ordered descending (newest first)
/// </param>
/// <param name="auditTypeFilter">
/// Since we currently do not have enum support with our expression parser, we cannot query on AuditType in the query or the custom filter
/// so we need to do that here
/// </param>
/// <param name="customFilter">
/// Optional filter to be applied
/// </param>
/// <returns></returns>
IEnumerable<IAuditItem> GetPagedItemsByUser(int userId, long pageIndex, int pageSize, out long totalRecords,
Direction orderDirection = Direction.Descending,
AuditType[] auditTypeFilter = null,
IQuery<IAuditItem> customFilter = null);
}
}
+15 -1
View File
@@ -642,7 +642,21 @@ namespace Umbraco.Core.Services
/// <param name="userId"></param>
/// <param name="raiseEvents"></param>
/// <returns>True if sorting succeeded, otherwise False</returns>
bool Sort(IEnumerable<IContent> items, int userId = 0, bool raiseEvents = true);
bool Sort(IEnumerable<IContent> items, int userId = 0, bool raiseEvents = true);
/// <summary>
/// Sorts a collection of <see cref="IContent"/> objects by updating the SortOrder according
/// to the ordering of node Ids passed in.
/// </summary>
/// <remarks>
/// Using this method will ensure that the Published-state is maintained upon sorting
/// so the cache is updated accordingly - as needed.
/// </remarks>
/// <param name="ids"></param>
/// <param name="userId"></param>
/// <param name="raiseEvents"></param>
/// <returns>True if sorting succeeded, otherwise False</returns>
bool Sort(int[] ids, int userId = 0, bool raiseEvents = true);
/// <summary>
/// Gets the parent of the current content as an <see cref="IContent"/> item.
+1 -30
View File
@@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Models;
@@ -12,35 +11,7 @@ namespace Umbraco.Core.Services
/// Defines the UserService, which is an easy access to operations involving <see cref="IProfile"/> and eventually Users.
/// </summary>
public interface IUserService : IMembershipUserService
{
/// <summary>
/// Creates a database entry for starting a new login session for a user
/// </summary>
/// <param name="userId"></param>
/// <param name="requestingIpAddress"></param>
/// <returns></returns>
Guid CreateLoginSession(int userId, string requestingIpAddress);
/// <summary>
/// Validates that a user login session is valid/current and hasn't been closed
/// </summary>
/// <param name="userId"></param>
/// <param name="sessionId"></param>
/// <returns></returns>
bool ValidateLoginSession(int userId, Guid sessionId);
/// <summary>
/// Removes the session's validity
/// </summary>
/// <param name="sessionId"></param>
void ClearLoginSession(Guid sessionId);
/// <summary>
/// Removes all valid sessions for the user
/// </summary>
/// <param name="userId"></param>
int ClearLoginSessions(int userId);
{
/// <summary>
/// This is basically facets of UserStates key = state, value = count
/// </summary>
+62 -5
View File
@@ -4,6 +4,7 @@ using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml.Linq;
using Umbraco.Core.Configuration;
@@ -11,6 +12,7 @@ using Umbraco.Core.Events;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
@@ -41,7 +43,7 @@ namespace Umbraco.Core.Services
if (userService == null) throw new ArgumentNullException("userService");
_dataTypeService = dataTypeService;
_userService = userService;
}
}
/// <summary>
/// Creates an <see cref="IMedia"/> object using the alias of the <see cref="IMediaType"/>
@@ -61,7 +63,7 @@ namespace Umbraco.Core.Services
{
var parent = GetById(parentId);
return CreateMedia(name, parent, mediaTypeAlias, userId);
}
}
/// <summary>
/// Creates an <see cref="IMedia"/> object using the alias of the <see cref="IMediaType"/>
@@ -729,10 +731,65 @@ namespace Umbraco.Core.Services
/// <returns><see cref="IMedia"/></returns>
public IMedia GetMediaByPath(string mediaPath)
{
var umbracoFileValue = mediaPath;
const string Pattern = ".*[_][0-9]+[x][0-9]+[.].*";
var isResized = Regex.IsMatch(mediaPath, Pattern);
// If the image has been resized we strip the "_403x328" of the original "/media/1024/koala_403x328.jpg" url.
if (isResized)
{
var underscoreIndex = mediaPath.LastIndexOf('_');
var dotIndex = mediaPath.LastIndexOf('.');
umbracoFileValue = string.Concat(mediaPath.Substring(0, underscoreIndex), mediaPath.Substring(dotIndex));
}
Func<string, Sql> createSql = url => new Sql().Select("*")
.From<PropertyDataDto>()
.InnerJoin<PropertyTypeDto>()
.On<PropertyDataDto, PropertyTypeDto>(left => left.PropertyTypeId, right => right.Id)
.Where<PropertyTypeDto>(x => x.Alias == "umbracoFile")
.Where<PropertyDataDto>(x => x.VarChar == url);
var sql = createSql(umbracoFileValue);
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
{
var repository = RepositoryFactory.CreateMediaRepository(uow);
return repository.GetMediaByPath(mediaPath);
var propertyDataDto = uow.Database.Fetch<PropertyDataDto, PropertyTypeDto>(sql).FirstOrDefault();
// If the stripped-down url returns null, we try again with the original url.
// Previously, the function would fail on e.g. "my_x_image.jpg"
if (propertyDataDto == null)
{
sql = createSql(mediaPath);
propertyDataDto = uow.Database.Fetch<PropertyDataDto, PropertyTypeDto>(sql).FirstOrDefault();
}
// If no reults far, try getting from a json value stored in the ntext column query
if (propertyDataDto == null)
{
var ntextQuery = new Sql().Select("*")
.From<PropertyDataDto>()
.InnerJoin<PropertyTypeDto>()
.On<PropertyDataDto, PropertyTypeDto>(left => left.PropertyTypeId, right => right.Id)
.Where<PropertyTypeDto>(x => x.Alias == "umbracoFile")
.Where("dataNtext LIKE @0", "%" + umbracoFileValue + "%");
propertyDataDto = uow.Database.Fetch<PropertyDataDto, PropertyTypeDto>(ntextQuery).FirstOrDefault();
}
// If still no results, try getting from a json value stored in the nvarchar column
if (propertyDataDto == null)
{
var nvarcharQuery = new Sql().Select("*")
.From<PropertyDataDto>()
.InnerJoin<PropertyTypeDto>()
.On<PropertyDataDto, PropertyTypeDto>(left => left.PropertyTypeId, right => right.Id)
.Where<PropertyTypeDto>(x => x.Alias == "umbracoFile")
.Where("dataNvarchar LIKE @0", "%" + umbracoFileValue + "%");
propertyDataDto = uow.Database.Fetch<PropertyDataDto, PropertyTypeDto>(nvarcharQuery).FirstOrDefault();
}
return propertyDataDto == null ? null : GetById(propertyDataDto.NodeId);
}
}
@@ -964,7 +1021,7 @@ namespace Umbraco.Core.Services
if (raiseEvents)
{
saveEventArgs.CanCancel = false;
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
}
+6 -2
View File
@@ -695,7 +695,7 @@ namespace Umbraco.Core.Services
IQuery<IMember> filterQuery = null;
if (filter.IsNullOrWhiteSpace() == false)
{
filterQuery = Query<IMember>.Builder.Where(x => x.Name.Contains(filter) || x.Username.Contains(filter));
filterQuery = Query<IMember>.Builder.Where(x => x.Name.Contains(filter) || x.Username.Contains(filter) || x.Email.Contains(filter));
}
var repository = RepositoryFactory.CreateMemberRepository(uow);
@@ -986,7 +986,11 @@ namespace Umbraco.Core.Services
/// <param name="raiseEvents">Optional parameter to raise events.
/// Default is <c>True</c> otherwise set to <c>False</c> to not raise events</param>
public void Save(IMember entity, bool raiseEvents = true)
{
{
//trimming username and email to make sure we have no trailing space
entity.Username = entity.Username.Trim();
entity.Email = entity.Email.Trim();
using (var uow = UowProvider.GetUnitOfWork())
{
var saveEventArgs = new SaveEventArgs<IMember>(entity);
@@ -1748,9 +1748,10 @@ namespace Umbraco.Core.Services
internal InstallationSummary InstallPackage(string packageFilePath, int userId = 0, bool raiseEvents = false)
{
var metaData = GetPackageMetaData(packageFilePath);
if (raiseEvents)
{
var metaData = GetPackageMetaData(packageFilePath);
{
if (ImportingPackage.IsRaisedEventCancelled(new ImportPackageEventArgs<string>(packageFilePath, metaData), this))
{
var initEmpty = new InstallationSummary().InitEmpty();
@@ -1762,7 +1763,7 @@ namespace Umbraco.Core.Services
if (raiseEvents)
{
ImportedPackage.RaiseEvent(new ImportPackageEventArgs<InstallationSummary>(installationSummary, false), this);
ImportedPackage.RaiseEvent(new ImportPackageEventArgs<InstallationSummary>(installationSummary, metaData, false), this);
}
return installationSummary;
+3 -44
View File
@@ -228,9 +228,9 @@ namespace Umbraco.Core.Services
}
/// <summary>
/// Deletes an <see cref="IUser"/>
/// Disables an <see cref="IUser"/>
/// </summary>
/// <param name="membershipUser"><see cref="IUser"/> to Delete</param>
/// <param name="membershipUser"><see cref="IUser"/> to disable</param>
public void Delete(IUser membershipUser)
{
//disable
@@ -558,47 +558,6 @@ namespace Umbraco.Core.Services
}
}
public Guid CreateLoginSession(int userId, string requestingIpAddress)
{
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateUserRepository(uow);
var session = repository.CreateLoginSession(userId, requestingIpAddress);
uow.Commit();
return session;
}
}
public int ClearLoginSessions(int userId)
{
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateUserRepository(uow);
var count = repository.ClearLoginSessions(userId);
uow.Commit();
return count;
}
}
public void ClearLoginSession(Guid sessionId)
{
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateUserRepository(uow);
repository.ClearLoginSession(sessionId);
uow.Commit();
}
}
public bool ValidateLoginSession(int userId, Guid sessionId)
{
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
{
var repository = RepositoryFactory.CreateUserRepository(uow);
return repository.ValidateLoginSession(userId, sessionId);
}
}
public IDictionary<UserState, int> GetUserStates()
{
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
@@ -753,7 +712,7 @@ namespace Umbraco.Core.Services
/// <param name="id">Id of the User to retrieve</param>
/// <returns><see cref="IProfile"/></returns>
public IProfile GetProfileById(int id)
{
{
//This is called a TON. Go get the full user from cache which should already be IProfile
var fullUser = GetUserById(id);
var asProfile = fullUser as IProfile;
+21 -3
View File
@@ -3,6 +3,7 @@ using System.Web;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.ObjectResolution;
namespace Umbraco.Core.Sync
@@ -46,8 +47,20 @@ namespace Umbraco.Core.Sync
// settings: for unit tests only
internal static void EnsureApplicationUrl(ApplicationContext appContext, HttpRequestBase request = null, IUmbracoSettingsSection settings = null)
{
bool newApplicationUrl = false;
if (request != null)
{
var applicationUrl = GetApplicationUrlFromRequest(request);
newApplicationUrl = !appContext._umbracoApplicationDomains.Contains(applicationUrl);
if (newApplicationUrl)
{
appContext._umbracoApplicationDomains.Add(applicationUrl);
LogHelper.Info(typeof(ApplicationUrlHelper), $"New ApplicationUrl detected: {applicationUrl}");
}
}
// if initialized, return
if (appContext._umbracoApplicationUrl != null) return;
if (appContext._umbracoApplicationUrl != null && !newApplicationUrl) return;
var logger = appContext.ProfilingLogger.Logger;
@@ -132,6 +145,12 @@ namespace Umbraco.Core.Sync
{
var logger = appContext.ProfilingLogger.Logger;
appContext._umbracoApplicationUrl = GetApplicationUrlFromRequest(request);
logger.Info(TypeOfApplicationUrlHelper, "ApplicationUrl: " + appContext.UmbracoApplicationUrl + " (UmbracoModule request)");
}
private static string GetApplicationUrlFromRequest(HttpRequestBase request)
{
// if (HTTP and SSL not required) or (HTTPS and SSL required),
// use ports from request
// otherwise,
@@ -147,8 +166,7 @@ namespace Umbraco.Core.Sync
var ssl = useSsl ? "s" : ""; // force, whatever the first request
var url = "http" + ssl + "://" + request.ServerVariables["SERVER_NAME"] + port + IOHelper.ResolveUrl(SystemDirectories.Umbraco);
appContext._umbracoApplicationUrl = url.TrimEnd('/');
logger.Info(TypeOfApplicationUrlHelper, "ApplicationUrl: " + appContext.UmbracoApplicationUrl + " (UmbracoModule request)");
return url.TrimEnd('/');
}
}
}
@@ -87,8 +87,7 @@ namespace Umbraco.Core.Sync
{
UtcStamp = DateTime.UtcNow,
Instructions = JsonConvert.SerializeObject(instructions, Formatting.None),
OriginIdentity = LocalIdentity,
InstructionCount = instructions.Sum(x => x.JsonIdCount)
OriginIdentity = LocalIdentity
};
ApplicationContext.DatabaseContext.Database.Insert(dto);
@@ -166,10 +165,10 @@ namespace Umbraco.Core.Sync
}
else
{
//check for how many instructions there are to process, each row contains a count of the number of instructions contained in each
//row so we will sum these numbers to get the actual count.
var count = _appContext.DatabaseContext.Database
.ExecuteScalar<int>("SELECT SUM(instructionCount) FROM umbracoCacheInstruction WHERE id > @lastId", new {lastId = _lastId});
//check for how many instructions there are to process
//TODO: In 7.6 we need to store the count of instructions per row since this is not affective because there can be far more than one (if not thousands)
// of instructions in a single row.
var count = _appContext.DatabaseContext.Database.ExecuteScalar<int>("SELECT COUNT(*) FROM umbracoCacheInstruction WHERE id > @lastId", new {lastId = _lastId});
if (count > Options.MaxProcessingInstructionCount)
{
//too many instructions, proceed to cold boot
@@ -206,7 +205,7 @@ namespace Umbraco.Core.Sync
/// <summary>
/// Synchronize the server (throttled).
/// </summary>
protected internal void Sync()
protected void Sync()
{
lock (_locko)
{
+4 -35
View File
@@ -18,10 +18,7 @@ namespace Umbraco.Core.Sync
// need this public, parameter-less constructor so the web service messenger
// can de-serialize the instructions it receives
public RefreshInstruction()
{
//set default - this value is not used for reading after it's been deserialized, it's only used for persisting the instruction to the db
JsonIdCount = 1;
}
{ }
// need this public one so it can be de-serialized - used by the Json thing
// otherwise, should use GetInstructions(...)
@@ -33,16 +30,12 @@ namespace Umbraco.Core.Sync
IntId = intId;
JsonIds = jsonIds;
JsonPayload = jsonPayload;
//set default - this value is not used for reading after it's been deserialized, it's only used for persisting the instruction to the db
JsonIdCount = 1;
}
private RefreshInstruction(ICacheRefresher refresher, RefreshMethodType refreshType)
{
RefresherId = refresher.UniqueIdentifier;
RefreshType = refreshType;
//set default - this value is not used for reading after it's been deserialized, it's only used for persisting the instruction to the db
JsonIdCount = 1;
}
private RefreshInstruction(ICacheRefresher refresher, RefreshMethodType refreshType, Guid guidId)
@@ -57,21 +50,9 @@ namespace Umbraco.Core.Sync
IntId = intId;
}
/// <summary>
/// A private constructor to create a new instance
/// </summary>
/// <param name="refresher"></param>
/// <param name="refreshType"></param>
/// <param name="json"></param>
/// <param name="idCount">
/// When the refresh method is <see cref="RefreshMethodType.RefreshByIds"/> we know how many Ids are being refreshed so we know the instruction
/// count which will be taken into account when we store this count in the database.
/// </param>
private RefreshInstruction(ICacheRefresher refresher, RefreshMethodType refreshType, string json, int idCount = 1)
private RefreshInstruction(ICacheRefresher refresher, RefreshMethodType refreshType, string json)
: this(refresher, refreshType)
{
JsonIdCount = idCount;
if (refreshType == RefreshMethodType.RefreshByJson)
JsonPayload = json;
else
@@ -96,12 +77,8 @@ namespace Umbraco.Core.Sync
case MessageType.RefreshById:
if (idType == null)
throw new InvalidOperationException("Cannot refresh by id if idType is null.");
if (idType == typeof(int))
{
// bulk of ints is supported
var intIds = ids.Cast<int>().ToArray();
return new[] { new RefreshInstruction(refresher, RefreshMethodType.RefreshByIds, JsonConvert.SerializeObject(intIds), intIds.Length) };
}
if (idType == typeof (int)) // bulk of ints is supported
return new[] { new RefreshInstruction(refresher, RefreshMethodType.RefreshByIds, JsonConvert.SerializeObject(ids.Cast<int>().ToArray())) };
// else must be guids, bulk of guids is not supported, iterate
return ids.Select(x => new RefreshInstruction(refresher, RefreshMethodType.RefreshByGuid, (Guid) x));
@@ -144,14 +121,6 @@ namespace Umbraco.Core.Sync
/// </summary>
public string JsonIds { get; set; }
/// <summary>
/// Gets or sets the number of Ids contained in the JsonIds json value
/// </summary>
/// <remarks>
/// This is used to determine the instruction count per row
/// </remarks>
public int JsonIdCount { get; set; }
/// <summary>
/// Gets or sets the payload data value.
/// </summary>
+4
View File
@@ -117,6 +117,8 @@ namespace Umbraco.Core
{
case UmbracoObjectTypes.Document:
return Document;
case UmbracoObjectTypes.DocumentBlueprint:
return DocumentBluePrint;
case UmbracoObjectTypes.Media:
return Media;
case UmbracoObjectTypes.Member:
@@ -161,6 +163,8 @@ namespace Umbraco.Core
{
case Document:
return UmbracoObjectTypes.Document;
case DocumentBluePrint:
return UmbracoObjectTypes.DocumentBlueprint;
case Media:
return UmbracoObjectTypes.Media;
case Member:
+7 -28
View File
@@ -39,18 +39,18 @@
<ItemGroup>
<Reference Include="AutoMapper, Version=3.3.1.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005, processorArchitecture=MSIL">
<HintPath>..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.dll</HintPath>
<Private>True</Private>
</Reference>
<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>
<Private>True</Private>
</Reference>
<Reference Include="ClientDependency.Core, Version=1.9.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ClientDependency.1.9.2\lib\net45\ClientDependency.Core.dll</HintPath>
<Reference Include="ClientDependency.Core, Version=1.9.5.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ClientDependency.1.9.5\lib\net45\ClientDependency.Core.dll</HintPath>
</Reference>
<Reference Include="HtmlAgilityPack, Version=1.4.9.5, Culture=neutral, PublicKeyToken=bd319b19eaf3b43a, processorArchitecture=MSIL">
<HintPath>..\packages\HtmlAgilityPack.1.4.9.5\lib\Net45\HtmlAgilityPack.dll</HintPath>
<Private>True</Private>
</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>
</Reference>
<Reference Include="ImageProcessor, Version=2.5.6.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ImageProcessor.2.5.6\lib\net45\ImageProcessor.dll</HintPath>
@@ -63,11 +63,9 @@
</Reference>
<Reference Include="Microsoft.AspNet.Identity.Core, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.Identity.Core.2.2.1\lib\net45\Microsoft.AspNet.Identity.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.AspNet.Identity.Owin, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.Identity.Owin.2.2.1\lib\net45\Microsoft.AspNet.Identity.Owin.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Owin, Version=3.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Owin.3.1.0\lib\net45\Microsoft.Owin.dll</HintPath>
@@ -83,17 +81,16 @@
</Reference>
<Reference Include="MiniProfiler, Version=2.1.0.0, Culture=neutral, PublicKeyToken=b44f9351044011a3, processorArchitecture=MSIL">
<HintPath>..\packages\MiniProfiler.2.1.0\lib\net40\MiniProfiler.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="MySql.Data, Version=6.9.9.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, processorArchitecture=MSIL">
<HintPath>..\packages\MySql.Data.6.9.9\lib\net45\MySql.Data.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Semver, Version=1.1.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\semver.1.1.2\lib\net45\Semver.dll</HintPath>
@@ -105,16 +102,12 @@
<Reference Include="System.Data.Entity" />
<Reference Include="System.Data.SqlServerCe, Version=4.0.0.1, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Data.SqlServerCe.Entity, Version=4.0.0.1, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.Entity.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.DirectoryServices.AccountManagement" />
<Reference Include="System.Drawing" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.Linq.Expressions" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Net.Http.WebRequest" />
@@ -246,7 +239,6 @@
<Compile Include="Configuration\UmbracoConfigurationSection.cs" />
<Compile Include="Configuration\UmbracoSettings\AppCodeFileExtensionsCollection.cs" />
<Compile Include="Configuration\UmbracoSettings\AppCodeFileExtensionsElement.cs" />
<Compile Include="Configuration\UmbracoSettings\BackOfficeElement.cs" />
<Compile Include="Configuration\UmbracoSettings\CharCollection.cs" />
<Compile Include="Configuration\UmbracoSettings\CharElement.cs" />
<Compile Include="Configuration\CommaDelimitedConfigurationElement.cs" />
@@ -255,7 +247,6 @@
<Compile Include="Configuration\UmbracoSettings\ContentErrorPageElement.cs" />
<Compile Include="Configuration\UmbracoSettings\ContentErrorsElement.cs" />
<Compile Include="Configuration\UmbracoSettings\ContentSectionExtensions.cs" />
<Compile Include="Configuration\UmbracoSettings\IBackOfficeSection.cs" />
<Compile Include="Configuration\UmbracoSettings\ImagingAutoFillPropertiesCollection.cs" />
<Compile Include="Configuration\UmbracoSettings\ImagingAutoFillUploadFieldElement.cs" />
<Compile Include="Configuration\UmbracoSettings\ContentImagingElement.cs" />
@@ -291,7 +282,6 @@
<Compile Include="Configuration\UmbracoSettings\ISecuritySection.cs" />
<Compile Include="Configuration\UmbracoSettings\IServer.cs" />
<Compile Include="Configuration\UmbracoSettings\ITemplatesSection.cs" />
<Compile Include="Configuration\UmbracoSettings\ITourSection.cs" />
<Compile Include="Configuration\UmbracoSettings\IUmbracoSettingsSection.cs" />
<Compile Include="Configuration\UmbracoSettings\IViewStateMoverModuleSection.cs" />
<Compile Include="Configuration\UmbracoSettings\IWebRoutingSection.cs" />
@@ -322,7 +312,6 @@
<Compile Include="Configuration\UmbracoSettings\ServerCollection.cs" />
<Compile Include="Configuration\UmbracoSettings\ServerElement.cs" />
<Compile Include="Configuration\UmbracoSettings\TemplatesElement.cs" />
<Compile Include="Configuration\UmbracoSettings\TourConfigElement.cs" />
<Compile Include="Configuration\UmbracoSettings\UmbracoConfigurationElement.cs" />
<Compile Include="Configuration\UmbracoSettings\UmbracoSettingsSection.cs" />
<Compile Include="Configuration\UmbracoSettings\UrlReplacingElement.cs" />
@@ -368,9 +357,9 @@
<Compile Include="HashGenerator.cs" />
<Compile Include="IEmailSender.cs" />
<Compile Include="IHttpContextAccessor.cs" />
<Compile Include="Logging\RollingFileCleanupAppender.cs" />
<Compile Include="Models\EntityBase\EntityPath.cs" />
<Compile Include="Models\EntityBase\IDeletableEntity.cs" />
<Compile Include="Models\IAuditItem.cs" />
<Compile Include="Models\IUserControl.cs" />
<Compile Include="Models\Membership\ContentPermissionSet.cs" />
<Compile Include="Models\Membership\EntityPermissionCollection.cs" />
@@ -382,8 +371,6 @@
<Compile Include="Models\Membership\UserState.cs" />
<Compile Include="Models\Membership\UserType.cs" />
<Compile Include="Models\PublishedContent\PublishedContentTypeConverter.cs" />
<Compile Include="Models\Rdbms\MediaDto.cs" />
<Compile Include="Models\Rdbms\UserLoginDto.cs" />
<Compile Include="Models\Rdbms\UserStartNodeDto.cs" />
<Compile Include="Models\UserControl.cs" />
<Compile Include="OrderedHashSet.cs" />
@@ -545,18 +532,12 @@
<Compile Include="Persistence\LockedRepository.cs" />
<Compile Include="Persistence\LockingRepository.cs" />
<Compile Include="Persistence\Mappers\AccessMapper.cs" />
<Compile Include="Persistence\Mappers\AuditMapper.cs" />
<Compile Include="Persistence\Mappers\DomainMapper.cs" />
<Compile Include="Persistence\Mappers\ExternalLoginMapper.cs" />
<Compile Include="Persistence\Mappers\MigrationEntryMapper.cs" />
<Compile Include="Persistence\Mappers\UserGroupMapper.cs" />
<Compile Include="Persistence\Migrations\LocalMigrationContext.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionFourOneZero\AddPreviewXmlTable.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenEightZero\AddTourDataUserColumn.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenEightZero\AddIndexToPropertyTypeAliasColumn.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenEightZero\AddInstructionCountColumn.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenEightZero\AddCmsMediaTable.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenEightZero\AddUserLoginTable.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSevenZero\AddIndexToDictionaryKeyColumn.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSevenZero\EnsureContentTemplatePermissions.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSevenZero\ReduceDictionaryKeyColumnsSize.cs" />
@@ -704,13 +685,11 @@
<Compile Include="Security\IBackOfficeUserPasswordChecker.cs" />
<Compile Include="Security\IMembershipProviderPasswordHasher.cs" />
<Compile Include="Security\IUserAwarePasswordHasher.cs" />
<Compile Include="Security\IUserSessionStore.cs" />
<Compile Include="Security\MachineKeyGenerator.cs" />
<Compile Include="Security\MembershipProviderPasswordHasher.cs" />
<Compile Include="Security\EmailService.cs" />
<Compile Include="Security\MembershipProviderPasswordValidator.cs" />
<Compile Include="Security\OwinExtensions.cs" />
<Compile Include="Security\SessionIdValidator.cs" />
<Compile Include="Security\UmbracoEmailMessage.cs" />
<Compile Include="Security\UserAwareMembershipProviderPasswordHasher.cs" />
<Compile Include="SemVersionExtensions.cs" />
+2 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="AutoMapper" version="3.3.1" targetFramework="net45" />
<package id="ClientDependency" version="1.9.2" targetFramework="net45" />
<package id="ClientDependency" version="1.9.5" targetFramework="net45" />
<package id="HtmlAgilityPack" version="1.4.9.5" targetFramework="net45" />
<package id="ImageProcessor" version="2.5.6" targetFramework="net45" />
<package id="log4net" version="2.0.8" targetFramework="net45" />
@@ -17,5 +17,6 @@
<package id="Newtonsoft.Json" version="10.0.2" targetFramework="net45" />
<package id="Owin" version="1.0" targetFramework="net45" />
<package id="semver" version="1.1.2" targetFramework="net45" />
<package id="SharpZipLib" version="0.86.0" targetFramework="net45" />
<package id="SqlServerCE" version="4.0.0.1" targetFramework="net45" />
</packages>
@@ -40,34 +40,27 @@
<ItemGroup>
<Reference Include="BenchmarkDotNet, Version=0.9.9.0, Culture=neutral, PublicKeyToken=aa0ca2f9092cefc4, processorArchitecture=MSIL">
<HintPath>..\packages\BenchmarkDotNet.0.9.9\lib\net45\BenchmarkDotNet.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="BenchmarkDotNet.Core, Version=0.9.9.0, Culture=neutral, PublicKeyToken=aa0ca2f9092cefc4, processorArchitecture=MSIL">
<HintPath>..\packages\BenchmarkDotNet.Core.0.9.9\lib\net45\BenchmarkDotNet.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="BenchmarkDotNet.Diagnostics.Windows, Version=0.9.9.0, Culture=neutral, PublicKeyToken=aa0ca2f9092cefc4, processorArchitecture=MSIL">
<HintPath>..\packages\BenchmarkDotNet.Diagnostics.Windows.0.9.9\lib\net45\BenchmarkDotNet.Diagnostics.Windows.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="BenchmarkDotNet.Toolchains.Roslyn, Version=0.9.9.0, Culture=neutral, PublicKeyToken=aa0ca2f9092cefc4, processorArchitecture=MSIL">
<HintPath>..\packages\BenchmarkDotNet.Toolchains.Roslyn.0.9.9\lib\net45\BenchmarkDotNet.Toolchains.Roslyn.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Castle.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<HintPath>..\packages\Castle.Core.4.0.0\lib\net45\Castle.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CodeAnalysis, Version=1.3.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.CodeAnalysis.Common.1.3.2\lib\net45\Microsoft.CodeAnalysis.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.CodeAnalysis.CSharp, Version=1.3.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.CodeAnalysis.CSharp.1.3.2\lib\net45\Microsoft.CodeAnalysis.CSharp.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Diagnostics.Tracing.TraceEvent, Version=1.0.41.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.1.0.41\lib\net40\Microsoft.Diagnostics.Tracing.TraceEvent.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Moq, Version=4.1.1309.919, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<HintPath>..\packages\Moq.4.1.1309.0919\lib\net40\Moq.dll</HintPath>
@@ -75,27 +68,22 @@
<Reference Include="System" />
<Reference Include="System.Collections.Immutable, Version=1.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Collections.Immutable.1.2.0\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Core" />
<Reference Include="System.Data.SqlServerCe, Version=4.0.0.1, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Data.SqlServerCe.Entity, Version=4.0.0.1, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.Entity.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Management" />
<Reference Include="System.Numerics" />
<Reference Include="System.Reflection.Metadata, Version=1.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Reflection.Metadata.1.3.0\lib\portable-net45+win8\System.Reflection.Metadata.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Threading.Tasks.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.0.0\lib\portable-net45+win8+wp8+wpa81\System.Threading.Tasks.Extensions.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
@@ -116,6 +104,7 @@
<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
<None Include="_TraceEventProgrammersGuide.docx" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Umbraco.Core\Umbraco.Core.csproj">
@@ -132,8 +121,8 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Analyzer Include="..\packages\Microsoft.CodeAnalysis.Analyzers.1.1.0\analyzers\dotnet\cs\Microsoft.CodeAnalysis.Analyzers.dll" />
<Analyzer Include="..\packages\Microsoft.CodeAnalysis.Analyzers.1.1.0\analyzers\dotnet\cs\Microsoft.CodeAnalysis.CSharp.Analyzers.dll" />
<Content Include="TraceEvent.ReadMe.txt" />
<Content Include="TraceEvent.ReleaseNotes.txt" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
@@ -121,7 +121,8 @@ namespace Umbraco.Tests.Models.Mapping
{
AssertDisplayProperty(result, p, ApplicationContext);
}
Assert.AreEqual(content.PropertyGroups.Count(), result.Tabs.Count());
Assert.AreEqual(content.PropertyGroups.Count(), result.Tabs.Count() - 1);
Assert.IsTrue(result.Tabs.Any(x => x.Label == ui.Text("general", "properties")));
Assert.IsTrue(result.Tabs.First().IsActive);
Assert.IsTrue(result.Tabs.Except(new[] {result.Tabs.First()}).All(x => x.IsActive == false));
}
@@ -1,47 +0,0 @@
using System;
using System.IO;
using System.Linq;
using NUnit.Framework;
using Umbraco.Core.Packaging;
namespace Umbraco.Tests.Packaging
{
[TestFixture]
class PackageExtractionTests
{
private const string PackageFileName = "Document_Type_Picker_1.1.umb";
private static string GetTestPackagePath(string packageName)
{
const string testPackagesDirName = "Packaging\\Packages";
string path = Path.Combine(Core.Configuration.GlobalSettings.FullpathToRoot, testPackagesDirName, packageName);
return path;
}
[Test]
public void ReadFilesFromArchive_NumberOfFilesIs1_SearchingForPackageXmlFile()
{
// Arrange
var sut = new PackageExtraction();
// Act
var result = sut.ReadFilesFromArchive(GetTestPackagePath(PackageFileName), new[] { "Package.xml" });
// Assert
Assert.AreEqual(1, result.Count());
}
[Test]
public void FindMissingFiles_1_UnknownFile()
{
// Arrange
var sut = new PackageExtraction();
// Act
var result = sut.FindMissingFiles(GetTestPackagePath(PackageFileName), new[] { "DoesNotExists.XYZ" });
// Assert
Assert.AreEqual(1, result.Count());
}
}
}
@@ -40,86 +40,5 @@ namespace Umbraco.Tests.Persistence.Repositories
Assert.That(dtos.Any(), Is.True);
Assert.That(dtos.First().Comment, Is.EqualTo("This is a System audit trail"));
}
[Test]
public void Get_Paged_Items()
{
var provider = new PetaPocoUnitOfWorkProvider(Logger);
var unitOfWork = provider.GetUnitOfWork();
using (var repo = new AuditRepository(unitOfWork, CacheHelper, Logger, SqlSyntax))
{
for (int i = 0; i < 100; i++)
{
repo.AddOrUpdate(new AuditItem(i, string.Format("Content {0} created", i), AuditType.New, 0));
repo.AddOrUpdate(new AuditItem(i, string.Format("Content {0} published", i), AuditType.Publish, 0));
}
unitOfWork.Commit();
}
using (var repo = new AuditRepository(unitOfWork, CacheHelper, Logger, SqlSyntax))
{
long total;
var page = repo.GetPagedResultsByQuery(Query<IAuditItem>.Builder, 0, 10, out total, Direction.Descending, null, null);
Assert.AreEqual(10, page.Count());
Assert.AreEqual(200, total);
}
}
[Test]
public void Get_Paged_Items_With_AuditType_Filter()
{
var provider = new PetaPocoUnitOfWorkProvider(Logger);
var unitOfWork = provider.GetUnitOfWork();
using (var repo = new AuditRepository(unitOfWork, CacheHelper, Logger, SqlSyntax))
{
for (int i = 0; i < 100; i++)
{
repo.AddOrUpdate(new AuditItem(i, string.Format("Content {0} created", i), AuditType.New, 0));
repo.AddOrUpdate(new AuditItem(i, string.Format("Content {0} published", i), AuditType.Publish, 0));
}
unitOfWork.Commit();
}
using (var repo = new AuditRepository(unitOfWork, CacheHelper, Logger, SqlSyntax))
{
long total;
var page = repo.GetPagedResultsByQuery(Query<IAuditItem>.Builder, 0, 9, out total, Direction.Descending,
new[] {AuditType.Publish}, null)
.ToArray();
Assert.AreEqual(9, page.Length);
Assert.IsTrue(page.All(x => x.AuditType == AuditType.Publish));
Assert.AreEqual(100, total);
}
}
[Test]
public void Get_Paged_Items_With_Custom_Filter()
{
var provider = new PetaPocoUnitOfWorkProvider(Logger);
var unitOfWork = provider.GetUnitOfWork();
using (var repo = new AuditRepository(unitOfWork, CacheHelper, Logger, SqlSyntax))
{
for (int i = 0; i < 100; i++)
{
repo.AddOrUpdate(new AuditItem(i, "Content created", AuditType.New, 0));
repo.AddOrUpdate(new AuditItem(i, "Content published", AuditType.Publish, 0));
}
unitOfWork.Commit();
}
using (var repo = new AuditRepository(unitOfWork, CacheHelper, Logger, SqlSyntax))
{
long total;
var page = repo.GetPagedResultsByQuery(Query<IAuditItem>.Builder, 0, 8, out total, Direction.Descending,
null, Query<IAuditItem>.Builder.Where(item => item.Comment == "Content created"))
.ToArray();
Assert.AreEqual(8, page.Length);
Assert.IsTrue(page.All(x => x.Comment == "Content created"));
Assert.AreEqual(100, total);
}
}
}
}
@@ -332,6 +332,9 @@ namespace Umbraco.Tests.Persistence.Repositories
var contentType = (IContentType)MockedContentTypes.CreateSimpleContentType2("test", "Test", propertyGroupName: "testGroup");
Assert.AreEqual(4, contentType.PropertyTypes.Count());
// remove all templates - since they are not saved, they would break the (wtf) mapping code
contentType.AllowedTemplates = new ITemplate[0];
// there is NO mapping from display to contentType, but only from save
// to contentType, so if we want to test, let's to it properly!
@@ -24,7 +24,9 @@ namespace Umbraco.Tests.Persistence.Repositories
[TestCase("Alpha (10)", "Alpha (2)", +1)] // this is the real stuff
[TestCase("Kilo", "Golf (2)", +1)]
[TestCase("Kilo (1)", "Golf (2)", +1)]
public void ComparerTest(string name1, string name2, int expected)
[TestCase("", "", 0)]
[TestCase(null, null, 0)]
public void ComparerTest(string name1, string name2, int expected)
{
var comparer = new SimilarNodeName.Comparer();
@@ -76,7 +78,9 @@ namespace Umbraco.Tests.Persistence.Repositories
[TestCase(0, "Alpha", "Alpha (3)")]
[TestCase(0, "Kilo (1)", "Kilo (1) (1)")] // though... we might consider "Kilo (2)"
[TestCase(6, "Kilo (1)", "Kilo (1)")] // because of the id
public void Test(int nodeId, string nodeName, string expected)
[TestCase(0, "", " (1)")]
[TestCase(0, null, " (1)")]
public void Test(int nodeId, string nodeName, string expected)
{
var names = new[]
{
@@ -271,7 +271,7 @@ AnotherContentFinder
public void Resolves_Assigned_Mappers()
{
var foundTypes1 = _manager.ResolveAssignedMapperTypes();
Assert.AreEqual(29, foundTypes1.Count());
Assert.AreEqual(28, foundTypes1.Count());
}
[Test]
@@ -29,7 +29,7 @@ namespace Umbraco.Tests.PropertyEditors
[Test]
public void ImageCropData_Properties_As_Dynamic()
{
var sourceObj = cropperJson1.SerializeToCropDataSet();
var sourceObj = cropperJson1.DeserializeToCropDataSet();
dynamic d = sourceObj;
var index = 0;
@@ -62,7 +62,7 @@ namespace Umbraco.Tests.PropertyEditors
[Test]
public void ImageCropFocalPoint_Properties_As_Dynamic()
{
var sourceObj = cropperJson1.SerializeToCropDataSet();
var sourceObj = cropperJson1.DeserializeToCropDataSet();
dynamic d = sourceObj;
Assert.AreEqual(sourceObj.FocalPoint.Left, d.FocalPoint.Left);
@@ -75,7 +75,7 @@ namespace Umbraco.Tests.PropertyEditors
[Test]
public void ImageCropDataSet_Properties_As_Dynamic()
{
var sourceObj = cropperJson1.SerializeToCropDataSet();
var sourceObj = cropperJson1.DeserializeToCropDataSet();
dynamic d = sourceObj;
Assert.AreEqual(sourceObj.Src, d.Src);
@@ -91,7 +91,7 @@ namespace Umbraco.Tests.PropertyEditors
[Test]
public void ImageCropDataSet_Methods_As_Dynamic()
{
var sourceObj = cropperJson1.SerializeToCropDataSet();
var sourceObj = cropperJson1.DeserializeToCropDataSet();
dynamic d = sourceObj;
Assert.AreEqual(sourceObj.HasCrop("thumb"), d.HasCrop("thumb"));
@@ -108,7 +108,7 @@ namespace Umbraco.Tests.PropertyEditors
public void CanConvertImageCropperDataSetSrcToString()
{
//cropperJson3 - has not crops
var sourceObj = cropperJson3.SerializeToCropDataSet();
var sourceObj = cropperJson3.DeserializeToCropDataSet();
var destObj = sourceObj.TryConvertTo<string>();
Assert.IsTrue(destObj.Success);
Assert.AreEqual(destObj.Result, "/media/1005/img_0672.jpg");
@@ -118,7 +118,7 @@ namespace Umbraco.Tests.PropertyEditors
public void CanConvertImageCropperDataSetJObject()
{
//cropperJson3 - has not crops
var sourceObj = cropperJson3.SerializeToCropDataSet();
var sourceObj = cropperJson3.DeserializeToCropDataSet();
var destObj = sourceObj.TryConvertTo<JObject>();
Assert.IsTrue(destObj.Success);
Assert.AreEqual(sourceObj, destObj.Result.ToObject<ImageCropDataSet>());
@@ -127,7 +127,7 @@ namespace Umbraco.Tests.PropertyEditors
[Test]
public void CanConvertImageCropperDataSetJsonToString()
{
var sourceObj = cropperJson1.SerializeToCropDataSet();
var sourceObj = cropperJson1.DeserializeToCropDataSet();
var destObj = sourceObj.TryConvertTo<string>();
Assert.IsTrue(destObj.Success);
Assert.IsTrue(destObj.Result.DetectIsJson());
@@ -150,7 +150,7 @@ namespace Umbraco.Tests.PropertyEditors
var converter = new Umbraco.Web.PropertyEditors.ValueConverters.ImageCropperValueConverter(dataTypeService.Object);
var result = converter.ConvertDataToSource(new PublishedPropertyType("test", 0, "test"), val1, false); // does not use type for conversion
var resultShouldMatch = val2.SerializeToCropDataSet();
var resultShouldMatch = val2.DeserializeToCropDataSet();
if (expected)
{
Assert.AreEqual(resultShouldMatch, result);
@@ -220,7 +220,7 @@ namespace Umbraco.Tests.PropertyEditors
[Test]
public void GetBaseCropUrlFromModelTest()
{
var cropDataSet = cropperJson1.SerializeToCropDataSet();
var cropDataSet = cropperJson1.DeserializeToCropDataSet();
var urlString = cropDataSet.GetCropUrl("thumb");
Assert.AreEqual("?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&width=100&height=100", urlString);
}
@@ -3,10 +3,13 @@ using System.Collections.Generic;
using System.Linq;
using Moq;
using NUnit.Framework;
using umbraco.cms.businesslogic;
using Umbraco.Core;
using Umbraco.Core.Events;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence;
using Umbraco.Core.Scoping;
using Umbraco.Core.Services;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.TestHelpers.Entities;
@@ -99,7 +102,7 @@ namespace Umbraco.Tests.Scoping
DoDeleteForContent += OnDoThingFail;
DoForTestArgs += OnDoThingFail;
DoForTestArgs2 += OnDoThingFail;
var contentType = MockedContentTypes.CreateBasicContentType();
var content1 = MockedContent.CreateBasicContent(contentType);
@@ -114,10 +117,10 @@ namespace Umbraco.Tests.Scoping
var scopeProvider = new ScopeProvider(Mock.Of<IDatabaseFactory2>());
using (var scope = scopeProvider.CreateScope(eventDispatcher: new PassiveEventDispatcher()))
{
//content1 will be filtered from the args
scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs<IContent>(new[]{ content1 , content3}));
scope.Events.Dispatch(DoDeleteForContent, this, new DeleteEventArgs<IContent>(content1));
scope.Events.Dispatch(DoDeleteForContent, this, new DeleteEventArgs<IContent>(content1), "DoDeleteForContent");
scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs<IContent>(content2));
//this entire event will be filtered
scope.Events.Dispatch(DoForTestArgs, this, new TestEventArgs(content1));
@@ -141,15 +144,36 @@ namespace Umbraco.Tests.Scoping
}
}
[Test]
public void SupersededEvents2()
{
var contentService = Mock.Of<IContentService>();
var content = Mock.Of<IContent>();
var l1 = new List<IEventDefinition>
{
new EventDefinition<IContentService, PublishEventArgs<IContent>>(Test_UnPublished, contentService, new PublishEventArgs<IContent>(new [] { content }), "UnPublished"),
new EventDefinition<IContentService, DeleteEventArgs<IContent>>(Test_Deleted, contentService, new DeleteEventArgs<IContent>(new [] { content }), "Deleted")
};
var l2 = new OrderedHashSet<IEventDefinition>(keepOldest: false);
foreach (var e in l1)
l2.Add(e);
var l3 = ScopeEventDispatcherBase.FilterSupersededAndUpdateToLatestEntity(l2);
// see U4-10764
Assert.AreEqual(2, l3.Count());
}
/// <summary>
/// This will test that when we track events that before we Get the events we normalize all of the
/// event entities to be the latest one (most current) found amongst the event so that there is
/// event entities to be the latest one (most current) found amongst the event so that there is
/// no 'stale' entities in any of the args
/// </summary>
[Test]
public void LatestEntities()
{
DoSaveForContent += OnDoThingFail;
DoSaveForContent += OnDoThingFail;
var now = DateTime.Now;
var contentType = MockedContentTypes.CreateBasicContentType();
@@ -165,7 +189,7 @@ namespace Umbraco.Tests.Scoping
var scopeProvider = new ScopeProvider(Mock.Of<IDatabaseFactory2>());
using (var scope = scopeProvider.CreateScope(eventDispatcher: new PassiveEventDispatcher()))
{
{
scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs<IContent>(content1));
scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs<IContent>(content2));
scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs<IContent>(content3));
@@ -173,7 +197,7 @@ namespace Umbraco.Tests.Scoping
// events have been queued
var events = scope.Events.GetEvents(EventDefinitionFilter.All).ToArray();
Assert.AreEqual(3, events.Length);
foreach (var t in events)
{
var args = (SaveEventArgs<IContent>)t.Args;
@@ -212,7 +236,7 @@ namespace Umbraco.Tests.Scoping
// events have been queued
var events = scope.Events.GetEvents(EventDefinitionFilter.FirstIn).ToArray();
Assert.AreEqual(1, events.Length);
Assert.AreEqual(1, events.Length);
Assert.AreEqual(content1, ((SaveEventArgs<IContent>) events[0].Args).SavedEntities.First());
Assert.IsTrue(object.ReferenceEquals(content1, ((SaveEventArgs<IContent>)events[0].Args).SavedEntities.First()));
Assert.AreEqual(content1.UpdateDate, ((SaveEventArgs<IContent>) events[0].Args).SavedEntities.First().UpdateDate);
@@ -350,6 +374,9 @@ namespace Umbraco.Tests.Scoping
public static event TypedEventHandler<ScopeEventDispatcherTests, SaveEventArgs<decimal>> DoThing3;
public static event TypedEventHandler<IContentService, PublishEventArgs<IContent>> Test_UnPublished;
public static event TypedEventHandler<IContentService, DeleteEventArgs<IContent>> Test_Deleted;
public class TestEventArgs : CancellableObjectEventArgs
{
public TestEventArgs(object eventObject) : base(eventObject)
@@ -22,7 +22,6 @@ namespace Umbraco.Tests.Security
public void Create_From_Claims_Identity()
{
var sessionId = Guid.NewGuid().ToString();
var securityStamp = Guid.NewGuid().ToString();
var claimsIdentity = new ClaimsIdentity(new[]
{
//This is the id that 'identity' uses to check for the user id
@@ -37,14 +36,12 @@ namespace Umbraco.Tests.Security
new Claim(ClaimTypes.Locality, "en-us", ClaimValueTypes.String, TestIssuer, TestIssuer),
new Claim(Constants.Security.SessionIdClaimType, sessionId, Constants.Security.SessionIdClaimType, TestIssuer, TestIssuer),
new Claim(ClaimsIdentity.DefaultRoleClaimType, "admin", ClaimValueTypes.String, TestIssuer, TestIssuer),
new Claim(Microsoft.AspNet.Identity.Constants.DefaultSecurityStampClaimType, securityStamp, ClaimValueTypes.String, TestIssuer, TestIssuer),
});
var backofficeIdentity = UmbracoBackOfficeIdentity.FromClaimsIdentity(claimsIdentity);
Assert.AreEqual("1234", backofficeIdentity.Id);
Assert.AreEqual(sessionId, backofficeIdentity.SessionId);
Assert.AreEqual(securityStamp, backofficeIdentity.SecurityStamp);
Assert.AreEqual("testing", backofficeIdentity.Username);
Assert.AreEqual("hello world", backofficeIdentity.RealName);
Assert.AreEqual(1, backofficeIdentity.StartContentNodes.Length);
@@ -53,7 +50,7 @@ namespace Umbraco.Tests.Security
Assert.AreEqual("en-us", backofficeIdentity.Culture);
Assert.IsTrue(new[] { "admin" }.SequenceEqual(backofficeIdentity.Roles));
Assert.AreEqual(11, backofficeIdentity.Claims.Count());
Assert.AreEqual(10, backofficeIdentity.Claims.Count());
}
[Test]
@@ -96,7 +93,6 @@ namespace Umbraco.Tests.Security
var sessionId = Guid.NewGuid().ToString();
var userData = new UserData(sessionId)
{
SecurityStamp = sessionId,
AllowedApplications = new[] {"content", "media"},
Culture = "en-us",
Id = 1234,
@@ -117,7 +113,6 @@ namespace Umbraco.Tests.Security
var sessionId = Guid.NewGuid().ToString();
var userData = new UserData(sessionId)
{
SecurityStamp = sessionId,
AllowedApplications = new[] { "content", "media" },
Culture = "en-us",
Id = 1234,
@@ -144,7 +139,6 @@ namespace Umbraco.Tests.Security
var sessionId = Guid.NewGuid().ToString();
var userData = new UserData(sessionId)
{
SecurityStamp = sessionId,
AllowedApplications = new[] { "content", "media" },
Culture = "en-us",
Id = 1234,
@@ -168,7 +162,6 @@ namespace Umbraco.Tests.Security
var sessionId = Guid.NewGuid().ToString();
var userData = new UserData(sessionId)
{
SecurityStamp = sessionId,
AllowedApplications = new[] { "content", "media" },
Culture = "en-us",
Id = 1234,
@@ -24,11 +24,9 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting
{
protected override Task<AuthenticationTicket> AuthenticateCoreAsync()
{
var sessionId = Guid.NewGuid().ToString();
var identity = new UmbracoBackOfficeIdentity(
new UserData(sessionId)
new UserData(Guid.NewGuid().ToString())
{
SecurityStamp = sessionId,
Id = 0,
Roles = new[] { "admin" },
AllowedApplications = new[] { "content", "media", "members" },

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