Compare commits

...

329 Commits

Author SHA1 Message Date
Sebastiaan Janssen eda7f1e527 Bump version 2017-09-19 14:17:19 +02:00
Sebastiaan Janssen 018a066f86 U4-10428 Can't drag Image in Image Cropper (in Umbraco 7.6.7) 2017-09-19 14:10:20 +02:00
Sebastiaan Janssen e33a28ac19 Making sure we don't build Belle twice for the documentation 2017-09-19 11:38:20 +02:00
Shannon 0ef57d819c removes uneeded null checks from AuthenticationController for raising UserManager events 2017-09-19 13:31:43 +10:00
Sebastiaan Janssen c321d6dd44 Adds files missing from build output 2017-09-18 16:52:43 +02:00
Sebastiaan Janssen 5d74e61bcf Bump version 2017-09-18 14:16:57 +02:00
Shannon 8b39f495e5 Simplifies the UnlockUser method so we don't have to have it in the BackOfficeUserManager 2017-09-18 19:59:23 +10:00
Shannon d468e346f1 reverts changes made to UmbracoMembershipProvider and adds another virtual internal method that the UsersMembershipProvider overrides, so now all of the event raising is done in the user specific provider. 2017-09-18 19:01:00 +10:00
Shannon 2a10eed059 Moves raising events for reseting/changing password out of the MembershipHelper ChangePassword method and to the places where it is being called 2017-09-18 16:56:43 +10:00
Shannon 4ec04c779a Adds notes about the UnlockUser method as we'll need to change this in 7.7 2017-09-18 16:29:29 +10:00
Shannon 26034fa33f Changes ResetAccessFailedCountAsync in BackOfficeUserManager to use Identity APIs 2017-09-18 16:24:23 +10:00
Shannon 97ddbdb1f0 Makes IdentityAuditEventArgs immutable and injects all required values, makes the events strongly typed. 2017-09-18 16:17:54 +10:00
Shannon Deminick 731c51bfc7 Merge pull request #2200 from abjerner/patch-21
Just had to have #2200 (Danish translations for the user section in Umbraco 7.7)
2017-09-18 15:05:43 +10:00
Shannon Deminick 78cb085e00 Merge pull request #2199 from abjerner/patch-20
Added missing Danish translation for "searchNoResult" in "general
2017-09-18 15:05:01 +10:00
Shannon Deminick f2ea4596d7 Merge pull request #2198 from abjerner/patch-19
Added translations for labels when creating a new partial view
2017-09-18 15:04:10 +10:00
Shannon Deminick f9d5de7af4 Merge pull request #2197 from abjerner/patch-18
Added Danish translation for "remove" in "general"
2017-09-18 15:03:38 +10:00
Anders Bjerner 04c6fd0947 Added Danish translation for "enabled" in the "actions" section
Also used in the new users section
2017-09-17 22:47:05 +02:00
Anders Bjerner 8bcf35734d Added some missing Danish translations for the new user section
There appears to be some hardcoded labels here and there, which may be for another pull request.
2017-09-17 22:45:38 +02:00
Anders Bjerner 4de7da2ac1 Added missing translation for "searchNoResult"
Used in Umbraco's list views when the search query doesn't match any items
2017-09-17 22:24:22 +02:00
Anders Bjerner 67d12ae2ca Added translations for labels when creating a new partial view
I think "partial view" and "snippet" still makes sense in Danish, so I've only translated the text around it.
2017-09-17 22:17:37 +02:00
Anders Bjerner e8ba069051 Added Danish translation for "remove" in "general"
The Danish translation is missing in both 7.6 and 7.7 beta. The label is used by the new content picker (and probably other parts of Umbraco as well):

![image](https://user-images.githubusercontent.com/3634580/30523147-64c23ce0-9bdc-11e7-9a48-8d2de93fce72.png)
2017-09-17 19:15:02 +02:00
Sebastiaan Janssen a3457d2498 Merge pull request #1923 from umbraco/temp-U4-8634
U4-8634 Usermanagement - Proper auditing of user events instead of just adding log entries
2017-09-15 15:13:14 +02:00
Sebastiaan Janssen 388d660e11 Raise event when login succeeds after 2FA challenge. Raise event when locked out during 2FA attempts. 2017-09-15 15:12:40 +02:00
Sebastiaan Janssen b6dbc245e2 Merge branch 'dev-v7' into temp-U4-8634 2017-09-15 13:57:46 +02:00
Sebastiaan Janssen 3efba6b576 Update title of preview window 2017-09-15 13:52:26 +02:00
Sebastiaan Janssen 54407a66a4 Fixes canvas designer 2017-09-15 13:50:43 +02:00
Sebastiaan Janssen bca46f11ad Makes the sort order input same size as the one on tabs 2017-09-14 16:11:23 +02:00
Warren Buckley a86b8f786d Merge pull request #1817 from marcemarc/temp-U4-6738
Relates to Issue U4-6738 - Allow viewing/sorting of composition properties.
2017-09-14 14:55:48 +01:00
Warren Buckley ac3eaf43cb Forgot to revert the logic for the extra button/label click to show the sort order on properties 2017-09-14 14:16:47 +01:00
Robert f6d4170010 Merge pull request #1633 from bjarnef/dev-v7-U4-9149
fixes: U4-9149 - Fix z-index of focal point
2017-09-14 15:15:51 +02:00
Warren Buckley 2b0ea45da9 Remove the extra button/click in the UI & show the property sortOrder value when we are sorting 2017-09-14 14:13:42 +01:00
Warren Buckley 07d05c9e0d Revert "toggle the show ordinals label button to hide, when in show ordinal mode"
This reverts commit 51215f11af.
2017-09-14 14:09:00 +01:00
Warren Buckley 26a5682a14 Revert "add language translations keys for showOrdinals and hideOrdinals"
This reverts commit 8ab2f7c257.
2017-09-14 14:08:00 +01:00
Sebastiaan Janssen f3fc2bcfb8 Merge pull request #2177 from umbraco/temp-U4-10384
U4-10384 Error when changing content type on media item
2017-09-14 13:46:32 +02:00
Robert c8d49ed5a7 Merge pull request #2195 from umbraco/temp-U4-10413
fixes: U4-10413 Composition/Reorder bar gets in the way when editing …
2017-09-14 11:41:04 +02:00
Mads Rasmussen 9c2b0a3ef6 fixes: U4-10413 Composition/Reorder bar gets in the way when editing or adding a property 2017-09-14 11:04:34 +02:00
Stephan 9355a81716 comments 2017-09-14 10:08:35 +02:00
Per Ploug 87575e9067 Reverting grid config 2017-09-14 10:04:04 +02:00
Sebastiaan Janssen a5b31e94b6 Merge pull request #2194 from umbraco/temp-U4-10409
temp - vsts troubleshooting
2017-09-14 09:32:04 +02:00
Shannon Deminick f9e094a00a Merge pull request #2193 from gunnim/dev-v7
U4-9562 - HealthCheckResolver from internal to public
2017-09-14 15:36:13 +10:00
Gunnar Már Óttarsson 053175a5a6 Fix http://issues.umbraco.org/issue/U4-9562
Change HealthCheckResolver to public allowing DI
2017-09-13 19:24:46 +00:00
Stephan ce7488a924 temp - vsts troubleshooting 2017-09-13 18:23:19 +02:00
Sebastiaan Janssen f442599633 Merge pull request #2191 from umbraco/temp-U4-10409
U4-10409 UDI Parsing should not require scanning assemblies
2017-09-13 17:28:33 +02:00
Sebastiaan Janssen 37b6b42570 Merge branch 'temp-U4-10409' of https://github.com/umbraco/Umbraco-CMS into temp-U4-10409 2017-09-13 17:20:49 +02:00
Stephan f1149fb826 U4-10409 - revert unwanted files 2017-09-13 17:20:06 +02:00
Sebastiaan Janssen 3f8f96e8c3 Merge branch 'dev-v7' into temp-U4-10409 2017-09-13 17:18:09 +02:00
Sebastiaan Janssen a75181bd7f Update sln to not refer to old build scripts any more 2017-09-13 13:07:22 +02:00
Sebastiaan Janssen 00b469b61b Easy ps script to just update the Umbraco version without having to do a full build 2017-09-13 12:07:29 +02:00
Sebastiaan Janssen 83f08538c2 U4-10407 We should log unhandled exceptions that occur in webapi controllers and always have detailed errors returned to users even when not in debug mode
# Conflicts:
#	src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs
2017-09-13 12:05:02 +02:00
Stephan ef91e152b4 U4-10409 - udi parsing and assembly scanning 2017-09-13 12:00:56 +02:00
Stephan add0685d3f U4-10409 - udi parsing 2017-09-13 09:35:17 +02:00
Shannon 27fc3a9bb3 U4-10409 UDI Parsing should not require scanning assemblies 2017-09-13 14:16:43 +10:00
Claus ac391f3bcd Merge branch 'temp-unittests-for-urls' into dev-v7 2017-09-12 15:04:08 +02:00
Claus 0700e37830 adding unit tests for urls.
adding unit test for creating child content without identity.
cleaning up some usings ...
2017-09-12 14:44:01 +02:00
Shannon Deminick 5f4985b4eb Merge pull request #2185 from tomvanenckevort/U4-10184
U4-10184 - Fixed string splitting in MultipleTextStringValueConverter.
2017-09-12 20:19:32 +10:00
Stephan d179792cc3 perfs - fix IsPublishable silly bug 2017-09-12 11:39:34 +02:00
Sebastiaan Janssen 4cb7b29f60 Try to make build server do the npm/gulp tasks on it's own 2017-09-12 11:20:46 +02:00
Sebastiaan Janssen fb2f9cea64 Make VSTS responsible for padding the build number 2017-09-12 11:03:56 +02:00
Sebastiaan Janssen 394f21f4be Make it possible for the build server to set an alpha build version 2017-09-12 10:38:22 +02:00
Claus 32cd9d44cb Merge pull request #1831 from leekelleher/issue/U4-9678
Fixes U4-9678 - XPath Syntax Parser slowdown
2017-09-12 10:19:36 +02:00
Sebastiaan Janssen 237b7176bf I see, we explicitly need to tell MsBuild not to build belle, fine 2017-09-12 09:19:34 +02:00
Tom van Enckevort 1fa2c16668 U4-10184 - Fixed string splitting in MultipleTextStringValueConverter. 2017-09-12 08:15:14 +01:00
Sebastiaan Janssen 1c5e2b5f73 No need for UmbracoBuild=True - the build scripts already manually build Belle once, this would just cause it to run again during MSBuild 2017-09-12 09:02:57 +02:00
Sebastiaan Janssen 30af8cb928 Only build Belle when UmbracoBuild is set (from powershell input), lib folder doesn't exist or on rebuild 2017-09-12 08:47:36 +02:00
Shannon 65b44137d4 Fixes JS unit test runner 2017-09-12 16:02:40 +10:00
Shannon Deminick 41eeb38c5f Merge pull request #2043 from umbraco/TEMP-u4-10135
Added TruncateByWords method + overloads
2017-09-12 13:36:00 +10:00
Shannon 4356d3f94a publicizes class, fixes up some naming and removes unneeded DirectoryInfo allocations 2017-09-12 13:30:57 +10:00
Shannon Deminick ba072aec7c Merge pull request #2183 from umbraco/temp-U4-10400
U4-10400 Clear CDF temp files during upgrades
2017-09-12 13:26:41 +10:00
Sebastiaan Janssen f1d3b1a6d3 Merge branch 'dev-v7' of https://github.com/umbraco/Umbraco-CMS into dev-v7 2017-09-11 16:23:08 +02:00
Sebastiaan Janssen 45f9919e57 Don't include pdb files in the built zip files 2017-09-11 16:21:51 +02:00
Sebastiaan Janssen 857b7cc608 Merge pull request #2003 from umbraco/temp-u4-10006
U4-10006 - VSTS/PS migration
2017-09-11 15:43:50 +02:00
Sebastiaan Janssen c23160f915 Merge remote-tracking branch 'origin/dev-v7' into temp-u4-10006
# Conflicts:
#	src/Umbraco.Web.UI.Client/test/unit/app/content/edit-content-controller.spec.js
#	src/Umbraco.Web.UI.Client/test/unit/app/media/edit-media-controller.spec.js
#	src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/content-picker-controller.spec.js
#	src/Umbraco.Web.UI.Client/test/unit/common/services/content-factory.spec.js
2017-09-11 15:08:15 +02:00
Mads Rasmussen 3e3a180fe4 found better check for unit test fixes 2017-09-11 14:35:17 +02:00
Sebastiaan Janssen c55cf75ada Use HttpContext from the controller 2017-09-11 13:30:06 +02:00
Sebastiaan Janssen 23950c0cac U4-10400 Clear CDF temp files during upgrades 2017-09-11 12:22:38 +02:00
Claus 082e399293 Merge pull request #1215 from IurieM/Add-error-calback-handler-for-unpublish-even-in-contetn.edit.controller
Add error callback handler for unpublish event in content.edit.controller
2017-09-11 11:51:09 +02:00
Mads Rasmussen c4ab25f1ac fixing first round of unit tests 2017-09-11 10:50:54 +02:00
Sebastiaan Janssen 3f55abaf59 Create directories only when they don't exist 2017-09-09 16:48:13 +02:00
Sebastiaan Janssen 001e15f83c Documentation build goes through gulp now too 2017-09-09 16:41:02 +02:00
Sebastiaan Janssen 34e3257ddb Ignore failing unit tests to check if VSTS will continue 2017-09-09 15:54:24 +02:00
Sebastiaan Janssen 2b64cfb4b9 Update with the new gulp build 2017-09-09 15:15:56 +02:00
Sebastiaan Janssen a499744084 Move BUILD.md to root, adds build.bat 2017-09-09 15:09:34 +02:00
Sebastiaan Janssen c72ecda18c VS is always install in {major}.0 - currently so when running 15.3, don't use the minor version in the path 2017-09-09 15:08:08 +02:00
Sebastiaan Janssen 3423d8b2cc Merge branch 'dev-v7' into temp-u4-10006
# Conflicts:
#	.gitignore
#	build/Build.bat
#	build/Build.proj
#	build/BuildBelle.bat
#	build/UmbracoVersion.txt
#	src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
2017-09-09 14:54:48 +02:00
Sebastiaan Janssen 37c4fc5f74 Don't try to remove directories that don't exist 2017-09-09 00:02:08 +02:00
Sebastiaan Janssen 406a206b03 Apparently we still have a node_modules folder, but no more xslt folder though! 2017-09-08 23:50:15 +02:00
Sebastiaan Janssen 55e0af118a Weirdly this errored out on 7.7 with a "Task 'cli' is not in your gulpfile" error..
not sure why this exact same file works in this branch
2017-09-08 23:29:16 +02:00
Sebastiaan Janssen 718ed19fb9 Forgot to remove this.. Seb is getting tired! 2017-09-08 15:05:15 +02:00
Sebastiaan Janssen 32999b7e19 Correction, we don't need this file after all :-) 2017-09-08 14:58:50 +02:00
Sebastiaan Janssen ce63251206 Adds missing file to gulp build 2017-09-08 14:53:33 +02:00
Sebastiaan Janssen 7b641f4771 Adds missing files for the build server 2017-09-08 14:45:00 +02:00
Sebastiaan Janssen 708d060c3b Fix up BuildBelle.bat 2017-09-08 14:12:54 +02:00
Sebastiaan Janssen e254dddbd4 Copy the js and lib files from the correct directories 2017-09-08 14:12:54 +02:00
Sebastiaan Janssen 87c2c5455c We don't have node_modules in this directory any more 2017-09-08 14:12:54 +02:00
Sebastiaan Janssen 8544aa685b VS loves managing whitespace.. go ahead then 2017-09-08 14:12:53 +02:00
Sebastiaan Janssen 09c23e93c2 Looks for the build output app.js to determine if we need to call BuildBelle.bat 2017-09-08 14:12:53 +02:00
Sebastiaan Janssen 9946d36d00 Ignore npm-debug.log 2017-09-08 14:12:53 +02:00
Sebastiaan Janssen 7012633fdd Install gulp before gulp-cli 2017-09-08 14:12:53 +02:00
Claus 6e348a3a1a Merge branch 'HarveyWilliams-patch-4' into dev-v7 2017-09-08 13:59:35 +02:00
Claus 733c920867 using string.replace instead of regex.
adding unit test.
2017-09-08 13:57:28 +02:00
Mads Rasmussen 35fc997d9f fix stylesheets for canvas designer/preview 2017-09-08 13:13:26 +02:00
Claus 921e8a2529 Merge pull request #2180 from umbraco/temp-U4-10392
U4-10392 Culture and hostnames with special characters can register a…
2017-09-08 11:27:25 +02:00
Claus 22bd6cd989 U4-10392 Culture and hostnames with special characters can register as collisions 2017-09-08 11:25:41 +02:00
Shannon 0713f12ba6 makes new classes internal 2017-09-08 12:02:20 +10:00
Sebastiaan Janssen dad4eafebd Enable RDP to figure out gulp build problem 2017-09-07 22:25:45 +02:00
Mads Rasmussen 96c9ecbdd7 fix calc functions 2017-09-07 21:39:41 +02:00
Sebastiaan Janssen e7e04de22e Merge pull request #2179 from umbraco/temp-dperfs
deploy-383 - dperfs
2017-09-07 20:46:31 +02:00
Sebastiaan Janssen 7003ef8d26 Remove circular dependency 2017-09-07 20:42:26 +02:00
Shannon Deminick 49a48bbfce Merge pull request #2169 from umbraco/temp-deploy-391
deploy-391 - allow for id reservation
2017-09-07 22:35:21 +10:00
Warren Buckley 663d9fcfe8 Merge pull request #2075 from defsteph/U4-10197
U4-10197 Added missing translations for Swedish
2017-09-07 13:33:55 +01:00
Stephan d6f8b878d2 perfs - cleanup 2017-09-07 13:45:56 +02:00
Claus 598944d3d8 ensuring id is only set if unset. 2017-09-07 13:13:03 +02:00
Claus 3caecf5161 U4-10384 Error when changing content type on media item 2017-09-07 12:57:06 +02:00
Stephan b02b2a61f0 perfs - deploy context isCancelled 2017-09-07 12:44:08 +02:00
Claus 60f7211a80 Merge branch 'patch-4' of git://github.com/HarveyWilliams/Umbraco-CMS into HarveyWilliams-patch-4 2017-09-07 10:36:49 +02:00
Sebastiaan Janssen 56dc10a4f7 Merge pull request #2158 from umbraco/temp-gulp-spike
U4-10261 Change frontend build setup from grunt to gulp
2017-09-06 23:52:48 +02:00
Stephan dbda6689b6 perfs - suspendable 2017-09-06 18:50:54 +02:00
Stephan 8d6c8ef282 perfs - support LocalDb 2017-09-06 18:50:41 +02:00
Warren Buckley df8c75438a Merge pull request #2168 from ronaldbarendse/U4-9374
U4-9374 Navigation panel header and editor panel header don't line up properly in Firefox
2017-09-06 12:37:45 +01:00
Warren Buckley 2d53da55a7 Merge pull request #2173 from tomfulton/u4-8886-2
U4-8886 - TinyMCE - highlight media picker toolbar icon when image selected
2017-09-06 12:25:01 +01:00
Robert 18d0fec014 Added some trimming and test to StripHtmlTags 2017-09-06 12:03:15 +02:00
Robert 851d587d1f Added Html Strip tests 2017-09-06 11:28:44 +02:00
Robert 6a3f94d0e1 Forgot to clean test names 2017-09-06 11:19:30 +02:00
Robert b1358936b4 Cleaning up tests and useless TruncateByWords functions 2017-09-06 11:17:06 +02:00
Robert b2798e38f7 Replaced html stripping with already existing function, removing unused code 2017-09-06 11:12:14 +02:00
Shannon a603fd0c46 Merge branch 'TEMP-u4-10135' of https://github.com/umbraco/Umbraco-CMS into TEMP-u4-10135
# Conflicts:
#	src/Umbraco.Web/HtmlStringUtilities.cs
2017-09-06 10:05:00 +10:00
Shannon 4b5c49907c switches to constant - comments out what seems to be a totally unused variable 2017-09-06 10:01:39 +10:00
Tom Fulton 1578bc8cc7 U4-8886 - TinyMCE - highlight media picker toolbar icon when image selected 2017-09-05 13:20:18 -04:00
Stephan e7c231c5a8 Support IdReservation in IdkMap 2017-09-05 15:23:18 +02:00
Sebastiaan Janssen a9cda9d651 Merge branch 'dev-v7' into temp-gulp-spike 2017-09-04 21:37:49 +02:00
Sebastiaan Janssen ad19eb8dff Update node version and remove unused npm reference 2017-09-04 20:14:31 +02:00
Sebastiaan Janssen e511554a42 Adds less as a dependency so that the build can complete 2017-09-04 20:10:19 +02:00
Stephan 84fd5dc2ee perfs - IContentService.IsPublishable 2017-09-04 19:49:28 +02:00
Stephan 344b52d10f deploy-391 - allow for id reservation 2017-09-04 17:04:48 +02:00
Sebastiaan Janssen 2121b7c8a0 Node version 2017-09-04 14:07:06 +02:00
mikkelhm 895faf41b1 Update version to 7.6.7 2017-09-04 12:28:16 +02:00
Stephan 9a06c3a5ff Implement ILocalizationService.GetDictionaryItemKeyMap 2017-09-04 11:31:20 +02:00
Sebastiaan Janssen 44102df36f Merge pull request #2167 from umbraco/temp-U4-10370
U4-10370 Culture and hostnames with "æøå" after /
2017-09-04 11:25:55 +02:00
Ronald Barendse 207d6b53b5 Fixed .umb-panel-header-content-wrapper height
The .umb-panel-header-content-wrapper was 1 pixel higher than the parent .umb-panel-header, now both heights are 99px.
2017-09-04 11:22:56 +02:00
Robert 972d5f8296 Reverted back to last commit, invalid html check added 2017-09-03 21:17:53 +02:00
Sebastiaan Janssen 617a91c57e Merge pull request #2119 from umbraco/temp-U4-10274
U4-10274 Umbraco.MemberHasAccess isn't cached
2017-09-01 15:45:55 +02:00
Sebastiaan Janssen cb0e5414b9 Merge pull request #2155 from umbraco/temp-U4-10361
U4-10361 Creating new member when `AllowManuallyChangingPassword="fal…
2017-09-01 14:46:58 +02:00
Claus 22cef52261 removing using. 2017-09-01 13:50:51 +02:00
Claus 41e419a49e U4-10370 Culture and hostnames with "æøå" after / 2017-09-01 13:48:39 +02:00
Stephan efbe97a01d Merge pull request #2149 from umbraco/temp-deploy-358
Updates the underlying event args base class's AdditionalData diction…
2017-08-31 18:25:07 +02:00
Stephan 0babb7f28a deploy-358 - reduce allocations in CancellableEventArgs 2017-08-31 18:03:34 +02:00
Stephan 5ba801386c Merge branch 'dev-v7' into temp-deploy-358 2017-08-31 18:03:19 +02:00
Robert 73937172dc Added check for invalid HTML and sanitization on said HTML
Added HtmlSanitization package
Added check for invalid HTML and sanitization on WordsToLength and Truncate methods
Addes some extra comments
2017-08-31 11:43:06 +02:00
Robert 8c1996a7a1 Changed from the String object qualifier to type qualifier 2017-08-31 09:15:02 +02:00
Robert 039ab8f1d2 Null check for firstTrim added and fixed typo 2017-08-30 09:18:45 +02:00
Shannon 8c998fd4e6 Creates a virtual ConfigureUmbracoUserManager in UmbracoDefaultOwinStartup so inheritors can just override that if they want to simplify the startup owin code process 2017-08-30 11:02:58 +10:00
Shannon Deminick 88f332f5c7 Merge pull request #2157 from umbraco/temp-U4-10367
U4-10367 417 missing token error due to cookie being overwritten
2017-08-30 10:54:29 +10:00
Mads Rasmussen 06a87bc341 update readme 2017-08-29 14:56:17 +02:00
Mads Rasmussen b10e646ade remove grunt file 2017-08-29 14:53:44 +02:00
Mads Rasmussen 110a3b2a12 add small interval to file watcher to keep windows calm 2017-08-29 14:35:54 +02:00
Mads Rasmussen 1bda9f32c1 only copy minified version of font-awesome 2017-08-29 14:27:43 +02:00
Mads Rasmussen ce8390768e Source maps gets cached really hard in the browser so I will remove them for now until we find a solution to clear them 2017-08-29 14:17:41 +02:00
Mads Rasmussen 7ccfc79080 update BuildBelle.bat to use gulp build task 2017-08-29 12:40:57 +02:00
Mads Rasmussen ed850629da Merge branch 'dev-v7' into temp-gulp-spike 2017-08-29 11:47:17 +02:00
Mads Rasmussen 1307cb1173 add themes for tinymce - they are needed for loading it 2017-08-29 11:39:16 +02:00
Sebastiaan Janssen a1b9842215 Merge pull request #2131 from bjarnef/dev-v7-U4-10281
U4-10281 - Fix maxchars javascript errors on macro parameter editors
2017-08-29 11:23:16 +02:00
Sebastiaan Janssen d300bf8d6d U4-10367 417 missing token error due to cookie being overwritten
Changes the cookie and header names to be Umbraco specific
2017-08-29 11:17:52 +02:00
Mads Rasmussen 70231fcee2 fix codemirror folder structure 2017-08-29 10:26:11 +02:00
Shannon 78e66ce25f ok, hope this fixes tests 2017-08-29 16:34:30 +10:00
Shannon dcc0374464 trying to fix unit tests 2017-08-29 15:18:16 +10:00
Shannon a95afa610f updates unit test config to work with new unit tests 2017-08-29 12:01:32 +10:00
Shannon 13e2112c3c U4-10361 Creating new member when AllowManuallyChangingPassword="false" - can't set a password 2017-08-29 11:56:38 +10:00
Shannon Deminick cf10f5f0ee Merge pull request #2154 from umbraco/temp-U4-10363
U4-10363 417 missing token error due to invalid cookie name
2017-08-29 11:11:35 +10:00
Shannon ca00246236 another update to perform a little better again (cookies are read VERY often) 2017-08-29 11:10:28 +10:00
Shannon d71b8a139f Moves GetCookieValue to an extension method, cleans up code, adds unit tests 2017-08-29 10:50:20 +10:00
Sebastiaan Janssen 6bf0d6e175 U4-10363 417 missing token error due to invalid cookie name
Manually parse the cookie header instead of using the buggy extension method
2017-08-28 20:36:56 +02:00
Mads Rasmussen aa5d7fc9fc only use source maps when developing 2017-08-28 15:42:31 +02:00
Mads Rasmussen 5cbe2b6ce0 Add tasks for karma testing 2017-08-28 14:41:54 +02:00
Sebastiaan Janssen 7a0c6ab975 Merge pull request #2135 from umbraco/temp-U4-10301
U4-10301 Change the PluginManager hash to use more reliable and consi…
2017-08-28 11:44:40 +02:00
Robert 68fd74897f Replaced the Regex parse with HtmlAgilityPack parser 2017-08-28 10:52:53 +02:00
Robert 047a41c05e Having a space in a tag is now taken as a char length and not as a space between words 2017-08-28 10:14:16 +02:00
Stephan 49d5005453 Merge pull request #2151 from umbraco/temp-U4-10358
U4-10358 Upgrade error from 7.1.8 to 7.6.5: The index 'IX_umbracoNode…
2017-08-26 15:26:41 +02:00
Sebastiaan Janssen f2db0fe80e U4-10358 Upgrade error from 7.1.8 to 7.6.5: The index 'IX_umbracoNodeUniqueID' is dependent on column 'uniqueID'
Removes migration that would add wrongly named index to a table, only to immediately be removed again in 7.3.0
2017-08-25 22:00:41 +02:00
Sebastiaan Janssen 9917fb20ca U4-10356 Update default Config/Lang after each update
Also makes sure UserExtensions returns the correct language
2017-08-25 16:13:04 +02:00
Mads Rasmussen 0089f79bd7 add tasks for ngdocs 2017-08-25 14:22:42 +02:00
Mads Rasmussen c468e4736e bower name should be lowercase 2017-08-25 14:19:25 +02:00
Robert 84f684ed31 Truncate with trimming should now work, added some mpore tests for truncate by words methods
Fix: Original Truncate method show now be able to trim
Add: Added some more tests for Truncate by words to cater for tagsAsContent parameter
Fix: In original truncate, currentTextLength prop will be increased everytime a tag is added, this will fix an issue when tagsAsContent is set to true (it would have added an extra char per tag, which would have went over to the next word)
2017-08-25 11:39:13 +02:00
Shannon e71e9d7efb U4-10249 FolderAndFilePermissions HealthCheck causes the application to restart
(cherry picked from commit bdb991f6dc)
2017-08-24 18:51:53 +02:00
Shannon 93cd03da28 minor code cleanup 2017-08-24 18:38:40 +10:00
Shannon 1dade7bfd2 fixes tests 2017-08-24 17:22:33 +10:00
Shannon c3b1aa36b8 Updates remaining services with one event arg instance for both event pairs to flow the EventState 2017-08-24 17:19:57 +10:00
Shannon 9cc466775d Updates more services to only have one event arg instance per event pair to flow through EventState 2017-08-24 16:50:12 +10:00
Shannon d0a91bf98b Updates ContentTypeService to ensure a single event args instance is created per event pair so that EventState flows. 2017-08-24 16:35:09 +10:00
Shannon 085c06e043 Adds EventState to the event args instead of trying to change the AdditionalData which is a breaking change. Updates FileService and ContentService to use one event arg insteance for both before/after events so that the EventState flows and so we are allocating less objects. 2017-08-24 16:22:50 +10:00
Shannon 1e45f85f7c Updates the underlying event args base class's AdditionalData dictionary to be IDictionary instead of ReadOnlyDictionary. Ensures the same args are used for both events when Deleting Templates. 2017-08-24 15:21:54 +10:00
Shannon a0785f2e0d backports U4-10349 Optimize EnsureUniqueNodeName 2017-08-23 16:22:01 +10:00
Mads Rasmussen 583302faa2 Merge pull request #2077 from abryukhov/temp-U4-10200
U4-10200 Listview layouts - empty state not localized
2017-08-21 21:37:01 +02:00
Sebastiaan Janssen 4404ce94c5 Merge pull request #2142 from abjerner/patch-17
Typo
2017-08-21 14:24:52 +02:00
Anders Bjerner 86bc701511 Typo
Very minor typo, but still ;)
2017-08-21 14:16:01 +02:00
Robert 54f86c06f0 Fix TruncateByWords to work with tags 2017-08-21 12:30:16 +02:00
Stephan a86902bd91 Merge pull request #2128 from umbraco/temp-U4-10289
U4-10289 IdkMap cache can cause the entity service to return invalid …
2017-08-18 21:45:35 +02:00
Mads Rasmussen 7bdf76ffa7 Merge pull request #2098 from bjarnef/dev-v7-U4-10242
U4-10242 - Remove bottom margin of subtitle paragraph
2017-08-18 14:21:08 +02:00
Claus 528656a337 using val.value directly. 2017-08-18 11:44:44 +02:00
Mads Rasmussen 5ef46ad8df Merge pull request #2048 from umbraco/temp-U4-7497
Pressing Enter/Backspace in repeatable text strings now adds/removes text boxes
2017-08-18 11:34:52 +02:00
Mads Rasmussen 168dc0e5da small UX refinements 2017-08-18 11:24:51 +02:00
Claus 5a1fc91f3e simplified it a bit. 2017-08-17 12:50:15 +02:00
Robert 5e6b5a5cc1 Added a check so it's not possibile to delete the texboxes that are set as minimum 2017-08-17 12:09:20 +02:00
Robert 7213e3a6a0 Adding and removing text boxes should work properly now
Thanks Mads :)
2017-08-17 12:06:32 +02:00
Mads Rasmussen 217ca62ea0 Merge pull request #2099 from bjarnef/dev-v7-U4-10152
U4-10152 - Fix typo in localization of placeholder text
2017-08-17 11:09:40 +02:00
Mads Rasmussen d216d5755c Merge pull request #2094 from skttl/patch-6
U4-10225: Notification banner floats above context menu
2017-08-17 10:55:37 +02:00
Claus 8db748b738 Merge pull request #2123 from umbraco/temp-U4-7382-2
U4-7382 Text Properties Containing JSON Are Corrupted on Publish
2017-08-17 10:43:51 +02:00
Shannon 300c9aa258 fix test 2017-08-17 12:10:34 +10:00
Shannon f7097d571c U4-10301 Change the PluginManager hash to use more reliable and consistent hashes 2017-08-17 11:38:57 +10:00
Mads Rasmussen b151c39c3b Merge pull request #2097 from skttl/patch-9
U4-10232 Unsaved changes alert overlaps borders
2017-08-16 16:15:49 +02:00
Claus bb01eca7b7 added a few tests for the new UmbracoHelper methods. 2017-08-15 13:08:30 +02:00
Claus 542406c402 Merge pull request #2130 from umbraco/temp-U4-10136
U4-10136 Broken link to package action documentation
2017-08-15 11:02:52 +02:00
Claus c44b903261 changed to private structs and made immutable. 2017-08-15 10:10:40 +02:00
Bjarne Fyrstenborg c68d7a33ca Fix maxchars javascript errors on macro parameter editors 2017-08-14 19:57:28 +02:00
Robert 2773102441 Fixed tag 2017-08-14 14:58:48 +02:00
Claus ef0d6eff2c Merge pull request #2124 from umbraco/mikecp-U4-8967
U4-8967 Linkpicker localization issue
2017-08-14 14:58:31 +02:00
Robert 1efad78214 Package-actions link updated 2017-08-14 14:58:05 +02:00
Claus 46b2d8a4aa implement equality comparison to prevent duplicates in when used in dictionaries. 2017-08-14 13:34:47 +02:00
Claus 7a5cb7058b U4-10289 IdkMap cache can cause the entity service to return invalid results 2017-08-14 11:38:13 +02:00
Harvey Williams 13f76d2af9 Updated ReplaceLineBreaksForHtml to include \r
I was surprised when this method did not replace a `\r` character with an HTML break. I have updated it to not only replace `\n`, but also `\r\n` and just `\r`.

I think these cases should be considered because of [this SO thread](https://stackoverflow.com/questions/4824621/is-a-new-line-n-or-r-n).

The updated code will not replace `\n\r` with one break but with two.

I used [this SO answer](https://stackoverflow.com/a/8196219/2963111) to update the method.

```
[TestMethod]
public void ReplaceLineBreaksForHtmlTest()
{
    var result = "foo\rbar\nbaz\r\nbiff\n\r".ReplaceLineBreaksForHtml();

    Assert.AreEqual(result, "foo<br />\rbar<br />\nbaz<br />\r\nbiff<br />\n<br />\r");
}
```
2017-08-11 11:42:35 +01:00
Robert c1c2339827 U4-8967 Linkpicker localization issue
Did a few modifications to the PR - reverted back the default translations for "content_nodeName" and "content_urls" as they are being used in other places.
To fix this, I have created two new keys that are used only in the linkpicker :)
2017-08-11 10:31:04 +02:00
Shannon 0e273d1a08 U4-7382 Text Properties Containing JSON Are Corrupted on Publish 2017-08-11 15:11:53 +10:00
Mads Rasmussen 125c378274 fixes: U4-4848 If "Save and Publish" is clicked before page finishes loading, data is lost from MNTP 2017-08-10 14:11:48 +02:00
mikkelhm ca80a0cddc Bump version to 7.6.6 2017-08-10 08:41:06 +02:00
Shannon 21707f9e08 Updates the GetMemberGroupsForMember with a username to be a single query 2017-08-10 14:22:30 +10:00
Shannon d8cfd8f12e U4-10274 Umbraco.MemberHasAccess isn't cached 2017-08-10 13:46:18 +10:00
Mads Rasmussen 08150c05e9 Merge pull request #1971 from Bitmapped/U4-9977
Fix U4-9977
2017-08-09 13:28:04 +02:00
Mads Rasmussen 688f8c4170 Merge pull request #2053 from bjarnef/dev-v7-U4-10161
U4-10161 - Update localization of email
2017-08-09 11:51:05 +02:00
Mads Rasmussen 31cbb79ede Translate zip-code 2017-08-09 11:48:15 +02:00
Shannon Deminick 305ce84ef5 Merge pull request #2111 from umbraco/temp-U4-10266
U4-10266 Package installer need to handle files before handling entities
2017-08-09 19:47:45 +10:00
Shannon c6d7056a64 Updated to return nulls as opposed to the ex being caught with a meaningful message 2017-08-09 19:44:37 +10:00
Bitmapped 7db1055a0d Remove JSON files, which appear to be unnecessary. 2017-08-08 13:54:36 -04:00
Claus d625388786 U4-10266 Package installer need to handle files before handling entities 2017-08-08 13:10:33 +02:00
Claus fa1663ed51 Fixes build script to work with VS 2017 (only) and fixes BuildBelle since it didn't work and is missing tools 2017-08-08 13:08:29 +02:00
Per Ploug babcbd2f7a Updates from dev-v7 2017-08-07 15:29:34 +02:00
Per Ploug ba82885b6d Merge remote-tracking branch 'refs/remotes/origin/dev-v7' into temp-gulp-spike
# Conflicts:
#	build/BuildBelle.bat
2017-08-07 14:05:53 +02:00
Bjarne Fyrstenborg 68ba0ca1e2 Fix typo in localization of placeholder text 2017-08-03 21:45:01 +02:00
Bjarne Fyrstenborg b22e9d0911 Remove bottom margin of subtitle paragraph 2017-08-03 21:35:03 +02:00
Søren Kottal 6a59a90964 Moves .alert-form.-no-border slightly
Moves .alert-form.-no-border slightly, so borders beneath are visible
2017-08-02 20:49:20 +02:00
Søren Kottal 26a042f291 U4-10225: Bumps z-index of #leftcolumn
http://issues.umbraco.org/issue/U4-10225

This bumps the z-index of #leftcolumn, so the notifications don't get in the way.
2017-08-02 19:41:55 +02:00
Alexander Bryukhov 6c91d54372 U4-10200 Listview layouts - empty state not localized 2017-07-26 10:24:03 +07:00
Stephan Lonntorp d4a2244190 Added missing translations 2017-07-25 13:28:46 +02:00
Stephan 14de3fee46 Merge branch dev-v7 into temp-u4-10006 2017-07-21 13:27:56 +02:00
Stephan 17d3548bbb Merge branch dev-v7 into temp-u4-10006 2017-07-21 10:04:25 +02:00
Stephan 9754c4511b U4-10006 - temp debug 2017-07-21 09:35:32 +02:00
Stephan 442d359ab0 U4-10006 - temp debug 2017-07-21 09:14:46 +02:00
Stephan bbed2fa8f6 U4-10006 - temp debug 2017-07-21 09:08:21 +02:00
Stephan 3089a0ab3a U4-10006 - temp debug 2017-07-21 08:48:09 +02:00
Bjarne Fyrstenborg 50d7114209 Update localization of email 2017-07-18 00:07:22 +02:00
Stephan b3ac3a03f3 U4-10006 - temp debug 2017-07-17 11:30:33 +02:00
Stephan bd0ca43b83 U4-10006 - use Invoke-WebRequest, misc 2017-07-17 09:46:09 +02:00
Stephan f39531216c U4-10006 - fix npm/node issues 2017-07-14 20:21:29 +02:00
Robert 84de083d52 Merge remote-tracking branch 'origin/temp-U4-7497' into temp-U4-7497 2017-07-14 13:57:36 +02:00
Robert 6ccb822cb5 On textbox delete the next in line gets focused
umbfocusbackwards.directive.js file added
2017-07-14 13:56:11 +02:00
Robert c94338d2b6 Newly added text box now get's focused. 2017-07-14 13:56:11 +02:00
Robert 3ba001164f Changed to keyup 2017-07-14 13:56:11 +02:00
Robert a8c0367492 Add/Remove functionality on key press added 2017-07-14 13:56:11 +02:00
Robert 91646212fd add/remove functionality on keyDown added 2017-07-14 13:56:11 +02:00
Robert 4c9b2f552a On textbox delete the next in line gets focused
umbfocusbackwards.directive.js file added
2017-07-14 13:30:28 +02:00
Stephan f24effdf0e U4-10006 - misc fixes 2017-07-14 10:17:40 +02:00
Stephan e9a4321ee8 U4-10006 - revert unwanted changes to tree.config 2017-07-14 10:17:13 +02:00
Robert 3395445d7c Newly added text box now get's focused. 2017-07-13 14:05:50 +02:00
Warren Buckley 9ea2fa9d8c Couple of tiny typos in the XML Comments/Docs 2017-07-13 10:15:41 +01:00
Robert cddc6f3116 Cleared useless methods 2017-07-12 12:28:40 +02:00
Robert 769f0d97d2 Added TruncateByWords method + overloads 2017-07-12 10:35:09 +02:00
Stephan f7d11bf936 U4-10006 - fix 2017-07-10 17:41:58 +02:00
Stephan 0fc868c9ba U4-10006 - verify NuGet 2017-07-10 15:58:22 +02:00
Stephan a3f4275b30 U4-10006 - build docs 2017-07-09 18:45:29 +02:00
Stephan cfd3301965 U4-10006 - fix build.ps1, update build.md doc 2017-07-09 17:29:06 +02:00
Stephan 48041c3163 U4-10006 - add build.ps1 2017-07-07 16:41:21 +02:00
Robert cc57cd6eb7 Changed to keyup 2017-07-05 12:57:29 +02:00
Robert 193eb259a6 Add/Remove functionality on key press added 2017-07-05 12:54:59 +02:00
Robert 6db18d83be add/remove functionality on keyDown added 2017-07-05 10:15:23 +02:00
Stephan 872530296f U4-10006 - fix 2017-06-29 19:26:51 +02:00
Stephan 71b5e5223f U4-10006 - fix 2017-06-28 16:26:27 +02:00
Stephan f3935b5007 Merge branch 'dev-v7' into temp-u4-10006 2017-06-28 13:10:05 +02:00
Per Ploug e35a748c92 Fixes issues with missing angular modules and adds sourcemap support 2017-06-27 17:00:00 +02:00
Stephan a3af1d478d U4-10006 - revert unrelated changes 2017-06-15 16:08:51 +02:00
Stephan 7d8fdff6c1 U4-10006 - adjust projects to new build 2017-06-15 15:50:50 +02:00
Stephan a813c0bfea U4-10006 - import some documentation 2017-06-15 15:15:53 +02:00
Stephan 4870554f47 U4-10006 - from build.bat to Umbraco.Build PS module 2017-06-15 13:32:00 +02:00
Sebastiaan Janssen 547767fc0f Fixes: when valid username is used, don't raise the unknown username event 2017-06-05 17:16:22 +02:00
Sebastiaan Janssen 74126134b1 Some more cleanup 2017-06-05 12:07:45 +02:00
Sebastiaan Janssen 6136118699 Clean up some events being raised, add code comments, shuffle some code around to make it look nicer 2017-06-05 11:52:42 +02:00
Sebastiaan Janssen 114b58c5ca Raise events for failed login attempts for unknown users 2017-06-05 10:43:57 +02:00
Sebastiaan Janssen 3019d67922 Adds forgot password events 2017-06-05 10:25:05 +02:00
Sebastiaan Janssen 97518c837f Merge remote-tracking branch 'origin/dev-v7' into temp-U4-8634 2017-06-04 16:55:42 +02:00
Sebastiaan Janssen 727250d2ab Removing AccountCreated and AccountUpdated for now, is the responsibility of UserService 2017-06-04 16:55:23 +02:00
Sebastiaan Janssen 66e3a00e56 Raise unlocking and saving events from EditUser.aspx 2017-06-04 15:18:24 +02:00
Sebastiaan Janssen aebea2da31 Merge remote-tracking branch 'origin/dev-v7' into temp-U4-8634 2017-06-04 14:35:35 +02:00
Sebastiaan Janssen 1634789641 Fix firing of locked out event 2017-06-04 14:33:13 +02:00
Brian Powell 70a0bbbe61 Change font references: icomoon -> tinymce 2017-06-03 12:49:42 -04:00
Brian Powell 24d9c60bdf Update fonts and stylesheet to include table icons missing per U4-9977 2017-06-01 21:59:31 -04:00
Mads Rasmussen 631a9f92ea fix merge bug + add ignore list 2017-05-05 14:19:37 +02:00
Mads Rasmussen 229a15be4f add nonodes to gulp 2017-05-05 13:10:40 +02:00
Mads Rasmussen 28d7a4a6dc Merge branch 'dev-v7.6' into temp-gulp-spike
# Conflicts:
#	src/Umbraco.Web.UI.Client/bower.json
2017-05-05 13:01:24 +02:00
Mads Rasmussen 21f003e705 clean up ace builds 2017-05-05 12:26:04 +02:00
Sebastiaan Janssen 0d80bf6a84 Alphabetize the events list and wire up the events correctly (instead of sending everything through OnAuthResetAccessFailedCount) 2017-04-30 12:26:33 +02:00
Sebastiaan Janssen e88f5c8fe2 Better way to get current user's IP address 2017-04-30 10:59:00 +02:00
Sebastiaan Janssen 7cb1a497e5 Merge remote-tracking branch 'origin/dev-v7.6' into temp-seb-authevents
# Conflicts:
#	src/Umbraco.Core/Security/BackOfficeSignInManager.cs
2017-04-30 10:17:07 +02:00
Sebastiaan Janssen b72c8ef953 Get userId more easily - also changed the default to -1 just in case we can't determine the Id (at least then changes won't be "blamed" on user 0 2017-04-30 10:15:53 +02:00
Sebastiaan Janssen 04a09bd4d6 Updates comment 2017-04-29 23:19:35 +02:00
Sebastiaan Janssen 311fcfe328 Adds AccountCreated event 2017-04-29 23:17:50 +02:00
Sebastiaan Janssen 33d09b14be Refactor GetBackofficeUserManager calls 2017-04-29 22:21:42 +02:00
Sebastiaan Janssen cdab89062e Make sure to only trigger events on MembershipHelper when we're dealing with backoffice users 2017-04-29 22:10:43 +02:00
Sebastiaan Janssen 38571f2cdd Wire up LoginSuccess and LoginRequiresVerification 2017-04-29 13:43:51 +02:00
Sebastiaan Janssen fcd90aa702 Get IP address from OWIN 2017-04-29 12:51:09 +02:00
Sebastiaan Janssen ae17172126 Attempt to raise events from parts of code that don't hit the BackOfficeUserManager directly 2017-04-29 12:49:56 +02:00
Sebastiaan Janssen ecca480334 Get backoffice identity from cookie instead of grabbing it from database 2017-04-29 12:25:35 +02:00
Sebastiaan Janssen 095a7c0aa7 First attempt at hooking up some events for auditing 2017-04-28 13:20:42 +02:00
leekelleher 169c6b6372 Fixes U4-9678 - XPath Syntax Parser slowdown
http://issues.umbraco.org/issue/U4-9678

Swaps out the base XPath from `"descendant::*[@id={0}]"` to `"id({0})"` - making use of the content-cache's XML DTD ID index.

This will perform better on larger content-trees.
2017-03-25 12:50:53 +00:00
Marc Goodson 8ab2f7c257 add language translations keys for showOrdinals and hideOrdinals 2017-03-24 10:21:01 +00:00
Marc Goodson 51215f11af toggle the show ordinals label button to hide, when in show ordinal mode 2017-03-24 10:20:31 +00:00
Marc Goodson 3df103eae2 In reorder mode, present an additional toggle button, to show the individual property editor sort ordinals, in input boxes, enabling their order to be manually overridden (for curating property order on tabs, when properties come from two different compositions) see U4-6738 2017-03-22 22:35:53 +00:00
Per Ploug 1757e7b4c8 Update gulpfile and buildbelle file
Should now have the right paths for running it from VS
2017-01-16 14:53:07 +01:00
Per Ploug a68eb91c14 update buildbell.bat file to use gulp 2017-01-10 14:58:46 +01:00
Per Ploug 7c9476ca41 Sets the correct root dir for compiling files 2017-01-09 15:27:05 +01:00
Per Ploug 5cee5a27c9 Wraps tasks in a merge-stream
This ensures proper timing and feedback of tasks
2017-01-09 15:26:05 +01:00
Per Ploug 1e34570a1f consistent naming for canvasdesigner js file 2017-01-07 01:28:16 +01:00
Per Ploug 7e930330e2 fixes karma testing config - now runs with "npm test" command instead 2017-01-07 01:27:59 +01:00
Per Ploug cc3355b1f9 fixes issues with bootstrap2 navbar and less2 syntax 2017-01-07 01:27:26 +01:00
Per Ploug ee9deacf92 ignore lib-bower temp folder 2017-01-07 01:26:47 +01:00
Per Ploug 4e9a8623f0 syntax issue in eslintrc 2017-01-07 01:26:35 +01:00
Per Ploug 904265cdf5 replaces gruntfile with gulpfile
Meaner, faster, better
2017-01-07 01:26:13 +01:00
Per Ploug 4a191f0711 Rebuild bower.json to support bower-installer
removes most of the custom bower lib installing we had in gruntfile
2017-01-07 01:25:43 +01:00
Per Ploug 6be9a25d99 new package.json to support gulp 2017-01-07 01:24:32 +01:00
bjarnef 89cbe53b63 Fix z-index of focal point 2016-11-22 23:00:25 +01:00
michael 3ce3a6f6ea Added files by mistake 2016-11-16 13:23:56 +01:00
michael b03e9f8fd5 issue U4-8967: updated labels as required and updated dialogs/linkpicker.html to be consistent with the structure overlay/linkpicker.html
I updated the labels in EN, FR, NL + other languages where I could figure out what was the update to perform.
2016-11-16 13:13:39 +01:00
Iurie Marchitan a58bf507ed Add error callback handler for unpublish event in content.edit.controller. For some cases user may not be allowed to unpublish a content, and he need to get a notification message that explains the reason. 2016-04-05 10:33:36 +02:00
253 changed files with 8050 additions and 3521 deletions
+6 -11
View File
@@ -46,13 +46,7 @@ src/Umbraco.Web.UI/Web.*.config.transformed
umbraco/presentation/umbraco/plugins/uComponents/uComponentsInstaller.ascx
umbraco/presentation/packages/uComponents/MultiNodePicker/CustomTreeService.asmx
_BuildOutput/*
*.ncrunchsolution
build/UmbracoCms.AllBinaries*zip
build/UmbracoCms.WebPI*zip
build/UmbracoCms*zip
build/UmbracoExamine.PDF*zip
build/*.nupkg
src/Umbraco.Tests/config/applications.config
src/Umbraco.Tests/config/trees.config
src/Umbraco.Web.UI/web.config
@@ -68,9 +62,9 @@ src/packages/repositories.config
src/Umbraco.Web.UI/[Ww]eb.config
*.transformed
webpihash.txt
node_modules
lib-bower
src/Umbraco.Web.UI/[Uu]mbraco/[Ll]ib/*
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/umbraco.*
@@ -95,7 +89,6 @@ src/Umbraco.Web.UI/[Uu]mbraco/[Aa]ssets/*
src/Umbraco.Web.UI.Client/[Bb]uild/*
src/Umbraco.Web.UI.Client/[Bb]uild/[Bb]elle/
src/Umbraco.Web.UI/[Uu]ser[Cc]ontrols/
build/_BuildOutput/
src/Umbraco.Web.UI.Client/src/[Ll]ess/*.css
tools/NDepend/
@@ -130,7 +123,6 @@ src/*.boltdata/
/src/Umbraco.Web.UI/Umbraco/Js/canvasdesigner.config.js
/src/Umbraco.Web.UI/Umbraco/Js/canvasdesigner.front.js
src/umbraco.sln.ide/*
build/UmbracoCms.*/
src/.vs/
src/Umbraco.Web.UI/umbraco/js/install.loader.js
src/Umbraco.Tests/media
@@ -140,8 +132,11 @@ apidocs/api/*
build/docs.zip
build/ui-docs.zip
build/csharp-docs.zip
build/msbuild.log
.vs/
src/packages/
build/tools/
src/PrecompiledWeb/*
build.out/
build.tmp/
build/Modules/*/temp/
+149
View File
@@ -0,0 +1,149 @@
Umbraco Cms Build
--
----
# Quick!
To build Umbraco, fire PowerShell and move to Umbraco's repository root (the directory that contains `src`, `build`, `README.md`...). There, trigger the build with the following command:
build\build.ps1
By default, this builds the current version. It is possible to specify a different version as a parameter to the build script:
build\build.ps1 7.6.44
Valid version strings are defined in the `Set-UmbracoVersion` documentation below.
## Notes
Git might have issues dealing with long file paths during build. You may want/need to enable `core.longpaths` support (see [this page](https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path) for details).
# Build
The Umbraco Build solution relies on a PowerShell module. The module needs to be imported into PowerShell. From within Umbraco's repository root:
build\build.ps1 -ModuleOnly
Or the abbreviated form:
build\build.ps1 -mo
Once the module has been imported, a set of commands are added to PowerShell.
## Get-UmbracoBuildEnv
Gets the Umbraco build environment ie NuGet, Semver, Visual Studio, etc. Downloads things that can be downloaded such as NuGet. Examples:
$uenv = Get-UmbracoBuildEnv
Write-Host $uenv.SolutionRoot
&$uenv.NuGet help
The object exposes the following properties:
* `SolutionRoot`: the absolute path to the solution root
* `VisualStudio`: a Visual Studio object (see below)
* `NuGet`: the absolute path to the NuGet executable
* `Zip`: the absolute path to the 7Zip executable
* `VsWhere`: the absolute path to the VsWhere executable
* `NodePath`: the absolute path to the Node install
* `NpmPath`: the absolute path to the Npm install
The Visual Studio object is `null` when Visual Studio has not been detected (eg on VSTS). When not null, the object exposes the following properties:
* `Path`: Visual Studio installation path (eg some place under `Program Files`)
* `Major`: Visual Studio major version (eg `15` for VS 2017)
* `Minor`: Visual Studio minor version
* `MsBUild`: the absolute path to the MsBuild executable
## Get-UmbracoVersion
Gets an object representing the current Umbraco version. Example:
$v = Get-UmbracoVersion
Write-Host $v.Semver
The object exposes the following properties:
* `Semver`: the semver object representing the version
* `Release`: the main part of the version (eg `7.6.33`)
* `Comment`: the pre release part of the version (eg `alpha02`)
* `Build`: the build number part of the version (eg `1234`)
## Set-UmbracoVersion
Modifies Umbraco files with the new version.
>This entirely replaces the legacy `UmbracoVersion.txt` file.
The version must be a valid semver version. It can include a *pre release* part (eg `alpha02`) and/or a *build number* (eg `1234`). Examples:
Set-UmbracoVersion 7.6.33
Set-UmbracoVersion 7.6.33-alpha02
Set-UmbracoVersion 7.6.33+1234
Set-UmbracoVersion 7.6.33-beta05+5678
Note that `Set-UmbracoVersion` enforces a slightly more restrictive naming scheme than what semver would tolerate. The pre release part can only be composed of a-z and 0-9, therefore `alpha033` is considered valid but not `alpha.033` nor `alpha033-preview` nor `RC2` (would need to be lowercased `rc2`).
>It is considered best to add trailing zeroes to pre releases, else NuGet gets the order of versions wrong. So if you plan to have more than 10, but no more that 100 alpha versions, number the versions `alpha00`, `alpha01`, etc.
## Build-Umbraco
Builds Umbraco. Temporary files are generated in `build.tmp` while the actual artifacts (zip files, NuGet packages...) are produced in `build.out`. Example:
Build-Umbraco
Some log files, such as MsBuild logs, are produced in `build.tmp` too. The `build` directory should remain clean during a build.
### web.config
Building Umbraco requires a clean `web.config` file in the `Umbraco.Web.UI` project. If a `web.config` file already exists, the `pre-build` task (see below) will save it as `web.config.temp-build` and replace it with a clean copy of `web.Template.config`. The original file is replaced once it is safe to do so, by the `pre-packages` task.
## Build-UmbracoDocs
Builds umbraco documentation. Temporary files are generated in `build.tmp` while the actual artifacts (docs...) are produced in `build.out`. Example:
Build-UmbracoDocs
Some log files, such as MsBuild logs, are produced in `build.tmp` too. The `build` directory should remain clean during a build.
## Verify-NuGet
Verifies that projects all require the same version of their dependencies, and that NuSpec files require versions that are consistent with projects. Example:
Verify-NuGet
# VSTS
Continuous integration, nightly builds and release builds run on VSTS.
VSTS uses the `Build-Umbraco` command several times, each time passing a different *target* parameter. The supported targets are:
* `pre-build`: prepares the build
* `compile-belle`: compiles Belle
* `compile-umbraco`: compiles Umbraco
* `pre-tests`: prepares the tests
* `compile-tests`: compiles the tests
* `pre-packages`: prepares the packages
* `pkg-zip`: creates the zip files
* `pre-nuget`: prepares NuGet packages
* `pkg-nuget`: creates NuGet packages
All these targets are executed when `Build-Umbraco` is invoked without a parameter (or with the `all` parameter). On VSTS, compilations (of Umbraco and tests) are performed by dedicated VSTS tasks. Similarly, creating the NuGet packages is also performed by dedicated VSTS tasks.
Finally, the produced artifacts are published in two containers that can be downloaded from VSTS: `zips` contains the zip files while `nuget` contains the NuGet packages.
>During a VSTS build, some environment `UMBRACO_*` variables are exported by the `pre-build` target and can be reused in other targets *and* in VSTS tasks. The `UMBRACO_TMP` environment variable is used in `Umbraco.Tests` to disable some tests that have issues with VSTS at the moment.
# Notes
*This part needs to be cleaned up*
Nightlies should use some sort of build number.
We should increment versions as soon as a version is released. Ie, as soon as `7.6.33` is released, we should `Set-UmbracoVersion 7.6.34-alpha` and push.
NuGet / NuSpec consistency checks are performed in tests. We should move it so it is done as part of the PowerShell script even before we try to compile and run the tests.
There are still a few commands in `build` (to build docs, install Git or cleanup the install) that will need to be migrated to PowerShell.
/eof
+1 -3
View File
@@ -12,9 +12,7 @@ Umbraco is a free open source Content Management System built on the ASP.NET pla
## Building Umbraco from source ##
The easiest way to get started is to run `build/build.bat` which will build both the backoffice (also known as "Belle") and the Umbraco core. You can then easily start debugging from Visual Studio, or if you need to debug Belle you can run `grunt vs` in `src\Umbraco.Web.UI.Client`.
If you're interested in making changes to Belle without running Visual Studio make sure to read the [Belle ReadMe file](src/Umbraco.Web.UI.Client/README.md).
The easiest way to get started is to run `build/build.bat` which will build both the backoffice (also known as "Belle") and the Umbraco core. You can then easily start debugging from Visual Studio, or if you need to debug Belle you can run `gulp dev` in `src\Umbraco.Web.UI.Client`.
Note that you can always [download a nightly build](http://nightly.umbraco.org/?container=umbraco-750) so you don't have to build the code yourself.
+4
View File
@@ -1,5 +1,9 @@
version: '{build}'
shallow_clone: true
init:
- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
build_script:
- cmd: >-
SET SLN=%CD%
+15
View File
@@ -0,0 +1,15 @@
@ECHO OFF
powershell .\build\build.ps1
IF ERRORLEVEL 1 (
GOTO :error
) ELSE (
GOTO :EOF
)
:error
ECHO.
ECHO Can not run build\build.ps1.
ECHO If this is due to a SecurityError then make sure to run the following command from an administrator command prompt:
ECHO.
ECHO powershell Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
-217
View File
@@ -1,217 +0,0 @@
@ECHO OFF
:: UMBRACO BUILD FILE
:: ensure we have UmbracoVersion.txt
IF NOT EXIST UmbracoVersion.txt (
ECHO UmbracoVersion.txt is missing!
GOTO error
)
REM Get the version and comment from UmbracoVersion.txt lines 2 and 3
SET RELEASE=
SET COMMENT=
FOR /F "skip=1 delims=" %%i IN (UmbracoVersion.txt) DO IF NOT DEFINED RELEASE SET RELEASE=%%i
FOR /F "skip=2 delims=" %%i IN (UmbracoVersion.txt) DO IF NOT DEFINED COMMENT SET COMMENT=%%i
REM process args
SET INTEGRATION=0
SET nuGetFolder=%CD%\..\src\packages
SET SKIPNUGET=0
:processArgs
:: grab the first parameter as a whole eg "/action:start"
:: end if no more parameter
SET SWITCHPARSE=%1
IF [%SWITCHPARSE%] == [] goto endProcessArgs
:: get switch and value
SET SWITCH=
SET VALUE=
FOR /F "tokens=1,* delims=: " %%a IN ("%SWITCHPARSE%") DO SET SWITCH=%%a& SET VALUE=%%b
:: route arg
IF '%SWITCH%'=='/release' GOTO argRelease
IF '%SWITCH%'=='-release' GOTO argRelease
IF '%SWITCH%'=='/comment' GOTO argComment
IF '%SWITCH%'=='-comment' GOTO argComment
IF '%SWITCH%'=='/integration' GOTO argIntegration
IF '%SWITCH%'=='-integration' GOTO argIntegration
IF '%SWITCH%'=='/nugetfolder' GOTO argNugetFolder
IF '%SWITCH%'=='-nugetfolder' GOTO argNugetFolder
IF '%SWITCH%'=='/skipnuget' GOTO argSkipNuget
IF '%SWITCH%'=='-skipnuget' GOTO argSkipNuget
ECHO "Invalid switch %SWITCH%"
GOTO error
:: handle each arg
:argRelease
set RELEASE=%VALUE%
SHIFT
goto processArgs
:argComment
SET COMMENT=%VALUE%
SHIFT
GOTO processArgs
:argIntegration
SET INTEGRATION=1
SHIFT
GOTO processArgs
:argNugetFolder
SET nuGetFolder=%VALUE%
SHIFT
GOTO processArgs
:argSkipNuget
SET SKIPNUGET=1
SHIFT
GOTO processArgs
:endProcessArgs
REM run
SET VERSION=%RELEASE%
IF [%COMMENT%] EQU [] (SET VERSION=%RELEASE%) ELSE (SET VERSION=%RELEASE%-%COMMENT%)
ECHO ################################################################
ECHO Building Umbraco %VERSION%
ECHO ################################################################
SET MSBUILDPATH=C:\Program Files (x86)\MSBuild\14.0\Bin
SET MSBUILD="%MSBUILDPATH%\MsBuild.exe"
SET PATH="%MSBUILDPATH%";%PATH%
ReplaceIISExpressPortNumber.exe ..\src\Umbraco.Web.UI\Umbraco.Web.UI.csproj %RELEASE%
ECHO.
ECHO Removing the belle build folder and bower_components folder to make sure everything is clean as a whistle
RD ..\src\Umbraco.Web.UI.Client\build /Q /S
RD ..\src\Umbraco.Web.UI.Client\bower_components /Q /S
ECHO.
ECHO Removing existing built files to make sure everything is clean as a whistle
RMDIR /Q /S _BuildOutput
DEL /F /Q UmbracoCms.*.zip 2>NUL
DEL /F /Q UmbracoExamine.*.zip 2>NUL
DEL /F /Q UmbracoCms.*.nupkg 2>NUL
DEL /F /Q webpihash.txt 2>NUL
ECHO.
ECHO Making sure Git is in the path so that the build can succeed
CALL InstallGit.cmd
REM Adding the default Git path so that if it's installed it can actually be found
REM This is necessary because SETLOCAL is on in InstallGit.cmd so that one might find Git,
REM but the path setting is lost due to SETLOCAL
SET PATH="C:\Program Files (x86)\Git\cmd";"C:\Program Files\Git\cmd";%PATH%
SET toolsFolder=%CD%\tools\
IF NOT EXIST "%toolsFolder%" (
MD tools
)
SET nuGetExecutable=%CD%\tools\nuget.exe
IF NOT EXIST "%nuGetExecutable%" (
ECHO Getting NuGet so we can fetch some tools
ECHO Downloading https://dist.nuget.org/win-x86-commandline/latest/nuget.exe to %nuGetExecutable%
powershell -Command "(New-Object Net.WebClient).DownloadFile('https://dist.nuget.org/win-x86-commandline/latest/nuget.exe', '%nuGetExecutable%')"
)
:: We need 7za.exe for BuildBelle.bat
IF NOT EXIST "%toolsFolder%7za.exe" (
ECHO 7zip not found - fetching now
"%nuGetExecutable%" install 7-Zip.CommandLine -OutputDirectory tools -Verbosity quiet
)
:: We need vswhere.exe for VS2017+
IF NOT EXIST "%toolsFolder%vswhere.exe" (
ECHO vswhere not found - fetching now
"%nuGetExecutable%" install vswhere -OutputDirectory tools -Verbosity quiet
)
:: Put 7za.exe and vswhere.exe in a predictable path (not version specific)
FOR /f "delims=" %%A in ('dir "%toolsFolder%7-Zip.CommandLine.*" /b') DO SET "sevenZipExePath=%toolsFolder%%%A\"
MOVE "%sevenZipExePath%tools\7za.exe" "%toolsFolder%7za.exe"
FOR /f "delims=" %%A in ('dir "%toolsFolder%vswhere.*" /b') DO SET "vswhereExePath=%toolsFolder%%%A\"
MOVE "%vswhereExePath%tools\vswhere.exe" "%toolsFolder%vswhere.exe"
ECHO.
ECHO Making sure we have a web.config
IF NOT EXIST "%CD%\..\src\Umbraco.Web.UI\web.config" COPY "%CD%\..\src\Umbraco.Web.UI\web.Template.config" "%CD%\..\src\Umbraco.Web.UI\web.config"
for /f "usebackq tokens=1* delims=: " %%i in (`"%CD%\tools\vswhere.exe" -latest -requires Microsoft.Component.MSBuild`) do (
if /i "%%i"=="installationPath" set InstallDir=%%j
)
SET VSWherePath="%InstallDir%\MSBuild"
ECHO.
ECHO Visual Studio is installed in: %InstallDir%
SET MSBUILDPATH=C:\Program Files (x86)\MSBuild\14.0\Bin
SET MSBUILD="%MSBUILDPATH%\MsBuild.exe"
ECHO.
ECHO Reporting NuGet version
"%nuGetExecutable%" help | findstr "^NuGet Version:"
ECHO.
ECHO Restoring NuGet packages
ECHO Into %nuGetFolder%
"%nuGetExecutable%" restore "%CD%\..\src\umbraco.sln" -Verbosity Quiet -NonInteractive -PackagesDirectory "%nuGetFolder%"
IF ERRORLEVEL 1 GOTO :error
ECHO.
ECHO.
ECHO Performing MSBuild and producing Umbraco binaries zip files
ECHO This takes a few minutes and logging is set to report warnings
ECHO and errors only so it might seems like nothing is happening for a while.
ECHO You can check the msbuild.log file for progress.
ECHO.
%MSBUILD% "Build.proj" /p:BUILD_RELEASE=%RELEASE% /p:BUILD_COMMENT=%COMMENT% /p:NugetPackagesDirectory="%nuGetFolder%" /p:VSWherePath=%VSWherePath% /consoleloggerparameters:Summary;ErrorsOnly /fileLogger
IF ERRORLEVEL 1 GOTO error
ECHO.
ECHO Setting node_modules folder to hidden to prevent VS13 from crashing on it while loading the websites project
attrib +h ..\src\Umbraco.Web.UI.Client\node_modules
IF %SKIPNUGET% EQU 1 GOTO success
ECHO.
ECHO Adding Web.config transform files to the NuGet package
REN .\_BuildOutput\WebApp\Views\Web.config Web.config.transform
REN .\_BuildOutput\WebApp\Xslt\Web.config Web.config.transform
ECHO.
ECHO Packing the NuGet release files
..\src\.nuget\NuGet.exe Pack NuSpecs\UmbracoCms.Core.nuspec -Version %VERSION% -Symbols -Verbosity quiet
..\src\.nuget\NuGet.exe Pack NuSpecs\UmbracoCms.nuspec -Version %VERSION% -Verbosity quiet
IF ERRORLEVEL 1 GOTO error
:success
ECHO.
ECHO No errors were detected!
ECHO There may still be some in the output, which you would need to investigate.
ECHO Warnings are usually normal.
ECHO.
ECHO.
GOTO :EOF
:error
ECHO.
ECHO Errors were detected!
ECHO.
REM don't pause if continuous integration else the build server waits forever
REM before cancelling the build (and, there is noone to read the output anyways)
IF %INTEGRATION% NEQ 1 PAUSE
-345
View File
@@ -1,345 +0,0 @@
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!--
****************************************************
INCLUDES
*****************************************************
-->
<PropertyGroup>
<MSBuildCommunityTasksPath>..\MSBuildCommunityTasks</MSBuildCommunityTasksPath>
<UmbracoMSBuildTasksPath>..\UmbracoMSBuildTasks</UmbracoMSBuildTasksPath>
</PropertyGroup>
<Import Project="..\tools\UmbracoMSBuildTasks\Umbraco.MSBuild.Tasks.Targets" />
<Import Project="..\tools\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
<UsingTask TaskName="GenerateHash" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<InputFiles ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
<OutputFile ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Using Namespace="System.IO" />
<Using Namespace="System.Linq" />
<Using Namespace="System.Security.Cryptography" />
<Code Type="Fragment" Language="cs">
<![CDATA[
using (var ms = new MemoryStream())
{
foreach (var item in InputFiles)
{
string path = item.ItemSpec;
using (FileStream stream = new FileStream(path, FileMode.Open))
{
using (var cryptoProvider = new SHA1CryptoServiceProvider())
{
var fileHash = cryptoProvider.ComputeHash(stream);
using (TextWriter w = new StreamWriter(OutputFile, false))
{
w.WriteLine(string.Join("", fileHash.Select(b => b.ToString("x2"))));
}
}
}
}
}
]]>
</Code>
</Task>
</UsingTask>
<!--
****************************************************
VARIABLES
*****************************************************
-->
<!-- NB: BUILD_NUMBER is passed in by the build server -->
<PropertyGroup Condition="'$(BUILD_NUMBER)'!=''">
<DECIMAL_BUILD_NUMBER>.$(BUILD_NUMBER)</DECIMAL_BUILD_NUMBER>
</PropertyGroup>
<PropertyGroup Condition="'$(BUILD_RELEASE)'!=''">
<DECIMAL_BUILD_NUMBER>.$(BUILD_RELEASE)</DECIMAL_BUILD_NUMBER>
</PropertyGroup>
<PropertyGroup Condition="'$(BUILD_RELEASE)'!='' AND '$(BUILD_COMMENT)'!=''">
<DECIMAL_BUILD_NUMBER>.$(BUILD_RELEASE)-$(BUILD_COMMENT)</DECIMAL_BUILD_NUMBER>
</PropertyGroup>
<PropertyGroup Condition="'$(BUILD_RELEASE)'!='' AND '$(BUILD_NIGHTLY)'!=''">
<DECIMAL_BUILD_NUMBER>.$(BUILD_RELEASE)-$(BUILD_NIGHTLY)</DECIMAL_BUILD_NUMBER>
</PropertyGroup>
<PropertyGroup Condition="'$(BUILD_RELEASE)'!='' AND '$(BUILD_COMMENT)'!='' AND '$(BUILD_NIGHTLY)'!=''">
<DECIMAL_BUILD_NUMBER>.$(BUILD_RELEASE)-$(BUILD_COMMENT)-$(BUILD_NIGHTLY)</DECIMAL_BUILD_NUMBER>
</PropertyGroup>
<PropertyGroup>
<BuildConfiguration>Release</BuildConfiguration>
<BuildFolder>_BuildOutput\</BuildFolder>
<BuildZipFileName>UmbracoCms$(DECIMAL_BUILD_NUMBER).zip</BuildZipFileName>
<BuildZipFileNameBin>UmbracoCms.AllBinaries$(DECIMAL_BUILD_NUMBER).zip</BuildZipFileNameBin>
<BuildZipFileNameWebPi>UmbracoCms.WebPI$(DECIMAL_BUILD_NUMBER).zip</BuildZipFileNameWebPi>
<IncludeSymbols>False</IncludeSymbols>
<BuildFolderRelativeToProjects>..\..\build\$(BuildFolder)</BuildFolderRelativeToProjects>
<BuildFolderAbsolutePath>$(MSBuildProjectDirectory)\$(BuildFolder)</BuildFolderAbsolutePath>
<SolutionBinFolder>$(BuildFolder)bin\</SolutionBinFolder>
<WebAppFolder>$(BuildFolder)WebApp\</WebAppFolder>
<WebPiFolder>$(BuildFolder)WebPi\</WebPiFolder>
<ConfigsFolder>$(BuildFolder)Configs\</ConfigsFolder>
<SolutionBinFolderRelativeToProjects>$(BuildFolderRelativeToProjects)bin\</SolutionBinFolderRelativeToProjects>
<SolutionBinFolderAbsolutePath>$(BuildFolderAbsolutePath)bin\</SolutionBinFolderAbsolutePath>
<WebAppFolderRelativeToProjects>$(BuildFolderRelativeToProjects)WebApp\</WebAppFolderRelativeToProjects>
<WebAppFolderAbsolutePath>$(BuildFolderAbsolutePath)WebApp\</WebAppFolderAbsolutePath>
<WebPiFolderRelativeToProjects>$(BuildFolderRelativeToProjects)WebPi\</WebPiFolderRelativeToProjects>
<WebPiFolderAbsolutePath>$(BuildFolderAbsolutePath)WebPi\</WebPiFolderAbsolutePath>
</PropertyGroup>
<ItemGroup>
<SystemFolders Include="$(WebAppFolder)App_Data" />
<SystemFolders Include="$(WebAppFolder)Media" />
<SystemFolders Include="$(WebAppFolder)Views" />
</ItemGroup>
<!--
****************************************************
TARGETS
*****************************************************
-->
<Target Name="Build" DependsOnTargets="GenerateWebPiHash">
<Message Text="Build finished" />
</Target>
<Target Name="GenerateWebPiHash" DependsOnTargets="ZipWebPiApp">
<ItemGroup>
<WebPiFile Include="$(BuildZipFileNameWebPi)" />
</ItemGroup>
<Message Text="Calculating hash for $(BuildZipFileNameWebPi)" />
<GenerateHash InputFiles="@(WebPiFile)" OutputFile="webpihash.txt" />
</Target>
<Target Name="CleanUp" DependsOnTargets="ZipWebPiApp">
<Message Text="Deleting $(BuildFolder)" Importance="high" />
<RemoveDir Directories="$(BuildFolder)" />
<Message Text="Finished deleting $(BuildFolder)" Importance="high" />
</Target>
<Target Name="ZipWebPiApp" DependsOnTargets="ZipWebApp" >
<!-- Clean folders -->
<RemoveDir Directories="$(WebPiFolder)" />
<MakeDir Directories="$(WebPiFolder)" />
<MakeDir Directories="$(WebPiFolder)umbraco" />
<!-- Copy fresh built umbraco files -->
<Exec Command="xcopy %22$(WebAppFolderAbsolutePath)*%22 %22$(WebPiFolderAbsolutePath)umbraco%22 /S /E /Y /I" />
<!-- Copy Web Pi template files -->
<ItemGroup>
<WebPiFiles Include="..\src\WebPi\**\*.*" />
</ItemGroup>
<Copy SourceFiles="@(WebPiFiles)"
DestinationFiles="@(WebPiFiles->'$(WebPiFolder)%(RecursiveDir)%(Filename)%(Extension)')" />
<!-- Zip the files -->
<Exec Command="..\tools\7zip\7za.exe a -r %22$(BuildZipFileNameWebPi)%22 %22$(WebPiFolderAbsolutePath)*%22 -x!dotLess.Core.dll -x![Content_Types].xml >NUL" />
</Target>
<Target Name="ZipWebApp" DependsOnTargets="CreateSystemFolders" >
<Message Text="Starting to zip to $(buildDate)-$(BuildZipFileName)" Importance="high" />
<Exec Command="..\tools\7zip\7za.exe a -r %22$(BuildZipFileNameBin)%22 %22$(SolutionBinFolderAbsolutePath)*%22 -x!dotLess.Core.dll >NUL" />
<Exec Command="..\tools\7zip\7za.exe a -r %22$(BuildZipFileName)%22 %22$(WebAppFolderAbsolutePath)*%22 -x!dotLess.Core.dll -x![Content_Types].xml >NUL" />
<Message Text="Finished zipping to build\$(BuildFolder)\$(buildDate)-$(BuildZipFileName)" Importance="high" />
</Target>
<Target Name="CreateSystemFolders" DependsOnTargets="CopyBelleBuild" Inputs="@(SystemFolders)" Outputs="%(Identity).Dummy">
<MakeDir Directories="@(SystemFolders)" />
</Target>
<Target Name="CopyBelleBuild" DependsOnTargets="CopyLibraries" >
<ItemGroup>
<BelleFiles Include="..\src\Umbraco.Web.UI.Client\build\belle\**\*.*" Exclude="..\src\Umbraco.Web.UI.Client\build\belle\index.html" />
</ItemGroup>
<Copy SourceFiles="@(BelleFiles)"
DestinationFiles="@(BelleFiles->'$(WebAppFolder)umbraco\%(RecursiveDir)%(Filename)%(Extension)')"
OverwriteReadOnlyFiles="true"
SkipUnchangedFiles="false" />
</Target>
<Target Name="CopyLibraries" DependsOnTargets="OffsetTimestamps" >
<!-- Copy SQL CE -->
<ItemGroup>
<SQLCE4Files
Include="..\src\packages\SqlServerCE.4.0.0.1\**\*.*"
Exclude="..\src\packages\SqlServerCE.4.0.0.1\lib\**\*;..\src\packages\SqlServerCE.4.0.0.1\**\*.nu*"
/>
</ItemGroup>
<Copy SourceFiles="@(SQLCE4Files)"
DestinationFiles="@(SQLCE4Files->'$(SolutionBinFolder)%(RecursiveDir)%(Filename)%(Extension)')"
OverwriteReadOnlyFiles="true"
SkipUnchangedFiles="false" />
<Copy SourceFiles="@(SQLCE4Files)"
DestinationFiles="@(SQLCE4Files->'$(WebAppFolder)bin\%(RecursiveDir)%(Filename)%(Extension)')"
OverwriteReadOnlyFiles="true"
SkipUnchangedFiles="false" />
</Target>
<!-- Offset the modified timestamps on all umbraco dlls, as WebResources break if date is in the future, which, due to timezone offsets can happen. -->
<Target Name="OffsetTimestamps" DependsOnTargets="CopyTransformedWebConfig">
<CreateItem Include="$(BuildFolder)**\umbraco.*.dll">
<Output TaskParameter="Include" ItemName="FilesToOffsetTimestamp" />
</CreateItem>
<Message Text="Starting to offset timestamps" Importance="high" />
<Umbraco.MSBuild.Tasks.TimestampOffset Files="@(FilesToOffsetTimestamp)" Offset="-11" />
<Message Text="Finished offsetting timestamps" Importance="high" />
</Target>
<!-- Copy the transformed web.config file to the root -->
<Target Name="CopyTransformedWebConfig" DependsOnTargets="CopyTransformedConfig">
<ItemGroup>
<WebConfigFile Include="..\src\Umbraco.Web.UI\web.$(BuildConfiguration).Config.transformed" />
</ItemGroup>
<Copy SourceFiles="@(WebConfigFile)"
DestinationFiles="$(WebAppFolder)Web.config"
OverwriteReadOnlyFiles="true"
SkipUnchangedFiles="false" />
</Target>
<!-- Copy the config files and rename them to *.config.transform -->
<Target Name="CopyTransformedConfig" DependsOnTargets="CopyXmlDocumentation">
<ItemGroup>
<ConfigFiles Include="$(WebAppFolder)config\*.config;$(WebAppFolder)config\*.js" />
<CustomLanguageFiles Include="$(WebAppFolder)config\lang\*.xml" />
<WebConfigTransformFile Include="$(WebAppFolder)Web.config" />
</ItemGroup>
<Copy SourceFiles="@(ConfigFiles)"
DestinationFiles="@(ConfigFiles->'$(ConfigsFolder)\%(RecursiveDir)%(Filename)%(Extension)')"
OverwriteReadOnlyFiles="true"
SkipUnchangedFiles="false" />
<Copy SourceFiles="@(CustomLanguageFiles)"
DestinationFiles="@(CustomLanguageFiles->'$(ConfigsFolder)Lang\%(RecursiveDir)%(Filename)%(Extension)')"
OverwriteReadOnlyFiles="true"
SkipUnchangedFiles="false" />
<Copy SourceFiles="@(WebConfigTransformFile)"
DestinationFiles="$(ConfigsFolder)Web.config.transform"
OverwriteReadOnlyFiles="true"
SkipUnchangedFiles="false" />
</Target>
<!-- Copy the xml documentation to the bin folder -->
<Target Name="CopyXmlDocumentation" DependsOnTargets="CleanupPresentation">
<ItemGroup>
<XmlDocumentationFiles Include="$(SolutionBinFolder)*.xml" />
</ItemGroup>
<Copy SourceFiles="@(XmlDocumentationFiles)"
DestinationFiles="@(XmlDocumentationFiles->'$(WebAppFolder)bin\%(RecursiveDir)%(Filename)%(Extension)')"
OverwriteReadOnlyFiles="true"
SkipUnchangedFiles="false" />
<Message Text="CopyXmlDocumentation" />
</Target>
<!-- Unlike 2010, the VS2012 build targets file doesn't clean up the umbraco.presentation dir, do it manually -->
<Target Name="CleanupPresentation" DependsOnTargets="CompileProjects">
<ItemGroup>
<PresentationFolderToDelete Include="$(WebAppFolder)umbraco.presentation" />
</ItemGroup>
<RemoveDir Directories="@(PresentationFolderToDelete)" />
</Target>
<Target Name="CompileProjects" DependsOnTargets="SetVersionNumber">
<Message Text="Compiling web project to build\$(BuildFolder)" Importance="high" />
<!-- For UseWPP_CopyWebApplication=True see http://stackoverflow.com/questions/1983575/copywebapplication-with-web-config-transformations -->
<!-- Build the Umbraco.Web.UI project -->
<MSBuild Projects="..\src\Umbraco.Web.UI\Umbraco.Web.UI.csproj" Properties="WarningLevel=0;Configuration=$(BuildConfiguration);UseWPP_CopyWebApplication=True;PipelineDependsOnBuild=False;OutDir=$(SolutionBinFolderAbsolutePath);WebProjectOutputDir=$(WebAppFolderAbsolutePath);Verbosity=minimal" Targets="Clean;Rebuild;" BuildInParallel="False" ToolsVersion="4.0" UnloadProjectsOnCompletion="False" />
<!-- DONE -->
<Message Text="Finished compiling projects" Importance="high" />
</Target>
<Target Name="SetVersionNumber" Condition="'$(BUILD_RELEASE)'!=''">
<PropertyGroup>
<NewVersion>$(BUILD_RELEASE)</NewVersion>
<NewVersion Condition="'$(BUILD_COMMENT)'!=''">$(BUILD_RELEASE)-$(BUILD_COMMENT)</NewVersion>
<NewVersion Condition="'$(BUILD_NIGHTLY)'!=''">$(BUILD_RELEASE)-$(BUILD_NIGHTLY)</NewVersion>
<NewVersion Condition="'$(BUILD_COMMENT)'!='' And '$(BUILD_NIGHTLY)'!=''">$(BUILD_RELEASE)-$(BUILD_COMMENT)-$(BUILD_NIGHTLY)</NewVersion>
</PropertyGroup>
<!-- Match & replace 3 and 4 digit version numbers and -beta and +nightly (if they're there) -->
<FileUpdate
Files="..\src\Umbraco.Core\Configuration\UmbracoVersion.cs"
Regex="(\d+)\.(\d+)\.(\d+)(.(\d+))?"
ReplacementText="$(BUILD_RELEASE)"/>
<FileUpdate Files="..\src\Umbraco.Core\Configuration\UmbracoVersion.cs"
Regex="CurrentComment { get { return &quot;(.+)?&quot;"
ReplacementText="CurrentComment { get { return &quot;$(BUILD_COMMENT)&quot;"/>
<FileUpdate Files="..\src\Umbraco.Core\Configuration\UmbracoVersion.cs"
Condition="'$(BUILD_NIGHTLY)'!=''"
Regex="CurrentComment { get { return &quot;(.+)?&quot;"
ReplacementText="CurrentComment { get { return &quot;$(BUILD_NIGHTLY)&quot;"/>
<FileUpdate Files="..\src\Umbraco.Core\Configuration\UmbracoVersion.cs"
Condition="'$(BUILD_COMMENT)'!='' AND '$(BUILD_NIGHTLY)'!=''"
Regex="CurrentComment { get { return &quot;(.+)?&quot;"
ReplacementText="CurrentComment { get { return &quot;$(BUILD_COMMENT)-$(BUILD_NIGHTLY)&quot;"/>
<!--This updates the AssemblyFileVersion for the solution to the umbraco version-->
<FileUpdate
Files="..\src\SolutionInfo.cs"
Regex="AssemblyFileVersion\(&quot;(.+)?&quot;\)"
ReplacementText="AssemblyFileVersion(&quot;$(BUILD_RELEASE)&quot;)"/>
<!--This updates the AssemblyInformationalVersion for the solution to the umbraco version and comment-->
<FileUpdate
Condition="'$(BUILD_COMMENT)'!=''"
Files="..\src\SolutionInfo.cs"
Regex="AssemblyInformationalVersion\(&quot;(.+)?&quot;\)"
ReplacementText="AssemblyInformationalVersion(&quot;$(BUILD_RELEASE)-$(BUILD_COMMENT)&quot;)"/>
<FileUpdate
Condition="'$(BUILD_COMMENT)'==''"
Files="..\src\SolutionInfo.cs"
Regex="AssemblyInformationalVersion\(&quot;(.+)?&quot;\)"
ReplacementText="AssemblyInformationalVersion(&quot;$(BUILD_RELEASE)&quot;)"/>
<FileUpdate
Condition="'$(BUILD_NIGHTLY)'!=''"
Files="..\src\SolutionInfo.cs"
Regex="AssemblyInformationalVersion\(&quot;(.+)?&quot;\)"
ReplacementText="AssemblyInformationalVersion(&quot;$(BUILD_RELEASE)-$(BUILD_NIGHTLY)&quot;)"/>
<FileUpdate
Condition="'$(BUILD_COMMENT)'!='' AND '$(BUILD_NIGHTLY)'!=''"
Files="..\src\SolutionInfo.cs"
Regex="AssemblyInformationalVersion\(&quot;(.+)?&quot;\)"
ReplacementText="AssemblyInformationalVersion(&quot;$(BUILD_RELEASE)-$(BUILD_COMMENT)-$(BUILD_NIGHTLY)&quot;)"/>
<FileUpdate
Condition="'$(BUILD_COMMENT)'=='' AND '$(BUILD_NIGHTLY)'==''"
Files="..\src\SolutionInfo.cs"
Regex="AssemblyInformationalVersion\(&quot;(.+)?&quot;\)"
ReplacementText="AssemblyInformationalVersion(&quot;$(BUILD_RELEASE)&quot;)"/>
<!--This updates the copyright year-->
<FileUpdate
Files="..\src\SolutionInfo.cs"
Regex="AssemblyCopyright\(&quot;Copyright © Umbraco (\d{4})&quot;\)"
ReplacementText="AssemblyCopyright(&quot;Copyright © Umbraco $([System.DateTime]::Now.ToString(`yyyy`))&quot;)"/>
<XmlPoke XmlInputPath=".\NuSpecs\build\UmbracoCms.props"
Namespaces="&lt;Namespace Prefix='x' Uri='http://schemas.microsoft.com/developer/msbuild/2003' /&gt;"
Query="//x:UmbracoVersion"
Value="$(NewVersion)" />
</Target>
</Project>
-59
View File
@@ -1,59 +0,0 @@
@ECHO OFF
SETLOCAL
:: SETLOCAL is on, so changes to the path not persist to the actual user's path
SET toolsFolder=%CD%\tools\
ECHO Current folder: %CD%
SET nodeFileName=node-v6.9.1-win-x86.7z
SET nodeExtractFolder=%toolsFolder%node.js.691
IF NOT EXIST "%nodeExtractFolder%" (
ECHO Downloading http://nodejs.org/dist/v6.9.1/%nodeFileName% to %toolsFolder%%nodeFileName%
powershell -Command "(New-Object Net.WebClient).DownloadFile('http://nodejs.org/dist/v6.9.1/%nodeFileName%', '%toolsFolder%%nodeFileName%')"
ECHO Extracting %nodeFileName% to %nodeExtractFolder%
"%toolsFolder%\7za.exe" x "%toolsFolder%\%nodeFileName%" -o"%nodeExtractFolder%" -aos > nul
)
FOR /f "delims=" %%A in ('dir "%nodeExtractFolder%\node*" /b') DO SET "nodePath=%nodeExtractFolder%\%%A"
SET nuGetExecutable=%CD%\tools\nuget.exe
IF NOT EXIST "%nuGetExecutable%" (
ECHO Downloading https://dist.nuget.org/win-x86-commandline/latest/nuget.exe to %nuGetExecutable%
powershell -Command "(New-Object Net.WebClient).DownloadFile('https://dist.nuget.org/win-x86-commandline/latest/nuget.exe', '%nuGetExecutable%')"
)
SET drive=%CD:~0,2%
SET nuGetFolder=%drive%\packages\
FOR /f "delims=" %%A in ('dir "%nuGetFolder%npm.*" /b') DO SET "npmPath=%nuGetFolder%%%A\"
IF [%npmPath%] == [] GOTO :installnpm
IF NOT [%npmPath%] == [] GOTO :build
:installnpm
ECHO Downloading npm
ECHO Configured packages folder: %nuGetFolder%
ECHO Installing Npm NuGet Package
"%nuGetExecutable%" install Npm -OutputDirectory %nuGetFolder% -Verbosity detailed
REM Ensures that we look for the just downloaded NPM, not whatever the user has installed on their machine
FOR /f "delims=" %%A in ('dir %nuGetFolder%npm.* /b') DO SET "npmPath=%nuGetFolder%%%A\"
GOTO :build
:build
ECHO Adding Npm and Node to path
REM SETLOCAL is on, so changes to the path not persist to the actual user's path
PATH="%npmPath%";"%nodePath%";%PATH%
SET buildFolder=%CD%
ECHO Change directory to %CD%\..\src\Umbraco.Web.UI.Client\
CD %CD%\..\src\Umbraco.Web.UI.Client\
ECHO Do npm install and the grunt build of Belle
call npm cache clean --quiet
call npm install --quiet
call npm install -g grunt-cli --quiet
call npm install -g bower --quiet
call grunt build --buildversion=%release%
ECHO Move back to the build folder
CD "%buildFolder%"
-20
View File
@@ -1,20 +0,0 @@
@ECHO OFF
SETLOCAL
SET release=%1
ECHO Installing Npm NuGet Package
SET nuGetFolder=%CD%\..\src\packages\
ECHO Configured packages folder: %nuGetFolder%
ECHO Current folder: %CD%
%CD%\..\src\.nuget\NuGet.exe install Npm.js -OutputDirectory %nuGetFolder% -Verbosity quiet
for /f "delims=" %%A in ('dir %nuGetFolder%node.js.* /b') do set "nodePath=%nuGetFolder%%%A\"
for /f "delims=" %%A in ('dir %nuGetFolder%npm.js.* /b') do set "npmPath=%nuGetFolder%%%A\tools\"
ECHO Adding Npm and Node to path
REM SETLOCAL is on, so changes to the path not persist to the actual user's path
PATH=%npmPath%;%nodePath%;%PATH%
Powershell.exe -ExecutionPolicy Unrestricted -File .\BuildDocs.ps1
-114
View File
@@ -1,114 +0,0 @@
$PSScriptFilePath = (Get-Item $MyInvocation.MyCommand.Path);
$RepoRoot = (get-item $PSScriptFilePath).Directory.Parent.FullName;
$SolutionRoot = Join-Path -Path $RepoRoot "src";
$ToolsRoot = Join-Path -Path $RepoRoot "tools";
$DocFx = Join-Path -Path $ToolsRoot "docfx\docfx.exe"
$DocFxFolder = (Join-Path -Path $ToolsRoot "docfx")
$DocFxJson = Join-Path -Path $RepoRoot "apidocs\docfx.json"
$7Zip = Join-Path -Path $ToolsRoot "7zip\7za.exe"
$DocFxSiteOutput = Join-Path -Path $RepoRoot "apidocs\_site\*.*"
$NgDocsSiteOutput = Join-Path -Path $RepoRoot "src\Umbraco.Web.UI.Client\docs\api\*.*"
$ProgFiles86 = [Environment]::GetEnvironmentVariable("ProgramFiles(x86)");
$MSBuild = "$ProgFiles86\MSBuild\14.0\Bin\MSBuild.exe"
################ Do the UI docs
"Changing to Umbraco.Web.UI.Client folder"
cd ..
cd src\Umbraco.Web.UI.Client
Write-Host $(Get-Location)
"Creating build folder so MSBuild doesn't run the whole grunt build"
if (-Not (Test-Path "build")) {
md "build"
}
"Installing node"
# Check if Install-Product exists, should only exist on the build server
if (Get-Command Install-Product -errorAction SilentlyContinue)
{
Install-Product node ''
}
"Installing node modules"
& npm install
"Installing grunt"
& npm install -g grunt-cli
"Moving back to build folder"
cd ..
cd ..
cd build
Write-Host $(Get-Location)
& grunt --gruntfile ../src/umbraco.web.ui.client/gruntfile.js docs
# change baseUrl
$BaseUrl = "https://our.umbraco.org/apidocs/ui/"
$IndexPath = "../src/umbraco.web.ui.client/docs/api/index.html"
(Get-Content $IndexPath).replace('location.href.replace(rUrl, indexFile)', "`'" + $BaseUrl + "`'") | Set-Content $IndexPath
# zip it
& $7Zip a -tzip ui-docs.zip $NgDocsSiteOutput -r
################ Do the c# docs
# Build the solution in debug mode
$SolutionPath = Join-Path -Path $SolutionRoot -ChildPath "umbraco.sln"
# Go get nuget.exe if we don't hae it
$NuGet = "$ToolsRoot\nuget.exe"
$FileExists = Test-Path $NuGet
If ($FileExists -eq $False) {
Write-Host "Retrieving nuget.exe..."
$SourceNugetExe = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
Invoke-WebRequest $SourceNugetExe -OutFile $NuGet
}
#restore nuget packages
Write-Host "Restoring nuget packages..."
& $NuGet restore $SolutionPath
& $MSBuild "$SolutionPath" /p:Configuration=Debug /maxcpucount /t:Clean
if (-not $?)
{
throw "The MSBuild process returned an error code."
}
& $MSBuild "$SolutionPath" /p:Configuration=Debug /maxcpucount
if (-not $?)
{
throw "The MSBuild process returned an error code."
}
# Go get docfx if we don't hae it
$FileExists = Test-Path $DocFx
If ($FileExists -eq $False) {
If(!(Test-Path $DocFxFolder))
{
New-Item $DocFxFolder -type directory
}
$DocFxZip = Join-Path -Path $ToolsRoot "docfx\docfx.zip"
$DocFxSource = "https://github.com/dotnet/docfx/releases/download/v1.9.4/docfx.zip"
Invoke-WebRequest $DocFxSource -OutFile $DocFxZip
#unzip it
& $7Zip e $DocFxZip "-o$DocFxFolder"
}
#clear site
If(Test-Path(Join-Path -Path $RepoRoot "apidocs\_site"))
{
Remove-Item $DocFxSiteOutput -recurse
}
# run it!
& $DocFx metadata $DocFxJson
& $DocFx build $DocFxJson
# zip it
& $7Zip a -tzip csharp-docs.zip $DocFxSiteOutput -r
@@ -0,0 +1,117 @@
#
function Build-UmbracoDocs
{
$uenv = Get-UmbracoBuildEnv
$src = "$($uenv.SolutionRoot)\src"
$out = "$($uenv.SolutionRoot)\build.out"
$tmp = "$($uenv.SolutionRoot)\build.tmp"
$buildTemp = "$PSScriptRoot\temp"
$cache = 2
Prepare-Build -keep $uenv
################ Do the UI docs
# No need to build belle again if it exists
$belleAppJs = "$src\Umbraco.Web.UI\Umbraco\js\app.js"
if(-not (Test-Path $belleAppJs))
{
# get a temp clean node env (will restore)
Sandbox-Node $uenv
push-location "$($uenv.SolutionRoot)\src\Umbraco.Web.UI.Client"
write "node version is:" > $tmp\belle.log
&node -v >> $tmp\belle.log 2>&1
write "npm version is:" >> $tmp\belle.log 2>&1
&npm -v >> $tmp\belle.log 2>&1
write "cleaning npm cache" >> $tmp\belle.log 2>&1
&npm cache clean >> $tmp\belle.log 2>&1
write "installing bower" >> $tmp\belle.log 2>&1
&npm install -g bower >> $tmp\belle.log 2>&1
write "installing gulp" >> $tmp\belle.log 2>&1
&npm install -g gulp >> $tmp\belle.log 2>&1
write "installing gulp-cli" >> $tmp\belle.log 2>&1
&npm install -g gulp-cli --quiet >> $tmp\belle.log 2>&1
write "executing npm install" >> $tmp\belle.log 2>&1
&npm install >> $tmp\belle.log 2>&1
write "building docs using gulp" >> $tmp\belle.log 2>&1
&gulp docs >> $tmp\belle.log 2>&1
pop-location
# fixme - should we filter the log to find errors?
#get-content .\build.tmp\belle-docs.log | %{ if ($_ -match "build") { write $_}}
} else {
Write-Host "Skipping belle build, $belleAppJs already exists"
}
# change baseUrl
$baseUrl = "https://our.umbraco.org/apidocs/ui/"
$indexPath = "$src/Umbraco.Web.UI.Client/docs/api/index.html"
(Get-Content $indexPath).Replace("location.href.replace(rUrl, indexFile)", "'$baseUrl'") `
| Set-Content $indexPath
# restore
Restore-Node
# zip
&$uenv.Zip a -tzip -r "$out\ui-docs.zip" "$src\Umbraco.Web.UI.Client\docs\api\*.*" `
> $null
################ Do the c# docs
Write-Host "Build C# documentation"
# Build the solution in debug mode
# FIXME no only a simple compilation should be enough!
# FIXME we MUST handle msbuild & co error codes!
# FIXME deal with weird things in gitconfig?
#Build-Umbraco -Configuration Debug
Restore-NuGet $uenv
Compile-Umbraco $uenv "Debug" # FIXME different log file!
Restore-WebConfig "$src\Umbraco.Web.UI"
# ensure we have docfx
Get-DocFx $uenv $buildTemp
# clear
$docFxOutput = "$($uenv.SolutionRoot)\apidocs\_site"
if (test-path($docFxOutput))
{
Remove-Directory $docFxOutput
}
# run
$docFxJson = "$($uenv.SolutionRoot)\apidocs\docfx.json"
push-location "$($uenv.SolutionRoot)\build" # silly docfx.json wants this
Write-Host "Run DocFx metadata"
Write-Host "Logging to $tmp\docfx.metadata.log"
&$uenv.DocFx metadata $docFxJson > "$tmp\docfx.metadata.log"
Write-Host "Run DocFx build"
Write-Host "Logging to $tmp\docfx.build.log"
&$uenv.DocFx build $docFxJson > "$tmp\docfx.build.log"
pop-location
# zip
&$uenv.Zip a -tzip -r "$out\csharp-docs.zip" "$docFxOutput\*.*" `
> $null
}
function Get-DocFx($uenv, $buildTemp)
{
$docFx = "$buildTemp\docfx"
if (-not (test-path $docFx))
{
Write-Host "Download DocFx..."
$source = "https://github.com/dotnet/docfx/releases/download/v2.19.2/docfx.zip"
Invoke-WebRequest $source -OutFile "$buildTemp\docfx.zip"
&$uenv.Zip x "$buildTemp\docfx.zip" -o"$buildTemp\docfx" -aos > $nul
Remove-File "$buildTemp\docfx.zip"
}
$uenv | add-member -memberType NoteProperty -name DocFx -value "$docFx\docfx.exe"
}
@@ -0,0 +1,180 @@
#
# Get-UmbracoBuildEnv
# Gets the Umbraco build environment
# Downloads tools if necessary
#
function Get-UmbracoBuildEnv
{
# store tools in the module's directory
# and cache them for two days
$path = "$PSScriptRoot\temp"
$cache = 2
if (-not (test-path $path))
{
mkdir $path > $null
}
# ensure we have NuGet
$nuget = "$path\nuget.exe"
$source = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
if ((test-path $nuget) -and ((ls $nuget).CreationTime -lt [DateTime]::Now.AddDays(-$cache)))
{
Remove-File $nuget
}
if (-not (test-path $nuget))
{
Write-Host "Download NuGet..."
Invoke-WebRequest $source -OutFile $nuget
}
# ensure we have 7-Zip
$sevenZip = "$path\7za.exe"
if ((test-path $sevenZip) -and ((ls $sevenZip).CreationTime -lt [DateTime]::Now.AddDays(-$cache)))
{
Remove-File $sevenZip
}
if (-not (test-path $sevenZip))
{
Write-Host "Download 7-Zip..."
&$nuget install 7-Zip.CommandLine -OutputDirectory $path -Verbosity quiet
$dir = ls "$path\7-Zip.CommandLine.*" | sort -property Name -descending | select -first 1
$file = ls -path "$dir" -name 7za.exe -recurse
mv "$dir\$file" $sevenZip
Remove-Directory $dir
}
# ensure we have vswhere
$vswhere = "$path\vswhere.exe"
if ((test-path $vswhere) -and ((ls $vswhere).CreationTime -lt [DateTime]::Now.AddDays(-$cache)))
{
Remove-File $vswhere
}
if (-not (test-path $vswhere))
{
Write-Host "Download VsWhere..."
&$nuget install vswhere -OutputDirectory $path -Verbosity quiet
$dir = ls "$path\vswhere.*" | sort -property Name -descending | select -first 1
$file = ls -path "$dir" -name vswhere.exe -recurse
mv "$dir\$file" $vswhere
Remove-Directory $dir
}
# ensure we have semver
$semver = "$path\Semver.dll"
if ((test-path $semver) -and ((ls $semver).CreationTime -lt [DateTime]::Now.AddDays(-$cache)))
{
Remove-File $semver
}
if (-not (test-path $semver))
{
Write-Host "Download Semver..."
&$nuget install semver -OutputDirectory $path -Verbosity quiet
$dir = ls "$path\semver.*" | sort -property Name -descending | select -first 1
$file = "$dir\lib\net452\Semver.dll"
if (-not (test-path $file))
{
Write-Error "Failed to file $file"
return
}
mv "$file" $semver
Remove-Directory $dir
}
try
{
[Reflection.Assembly]::LoadFile($semver) > $null
}
catch
{
Write-Error -Exception $_.Exception -Message "Failed to load $semver"
break
}
# ensure we have node
$node = "$path\node-v6.9.1-win-x86"
$source = "http://nodejs.org/dist/v6.9.1/node-v6.9.1-win-x86.7z"
if (-not (test-path $node))
{
Write-Host "Download Node..."
Invoke-WebRequest $source -OutFile "$path\node-v6.9.1-win-x86.7z"
&$sevenZip x "$path\node-v6.9.1-win-x86.7z" -o"$path" -aos > $nul
Remove-File "$path\node-v6.9.1-win-x86.7z"
}
# note: why? node already brings everything we need!
## ensure we have npm
#$npm = "$path\npm.*"
#$getNpm = $true
#if (test-path $npm)
#{
# $getNpm = $false
# $tmpNpm = ls "$path\npm.*" | sort -property Name -descending | select -first 1
# if ($tmpNpm.CreationTime -lt [DateTime]::Now.AddDays(-$cache))
# {
# $getNpm = $true
# }
# else
# {
# $npm = $tmpNpm.ToString()
# }
#}
#if ($getNpm)
#{
# Write-Host "Download Npm..."
# &$nuget install npm -OutputDirectory $path -Verbosity quiet
# $npm = ls "$path\npm.*" | sort -property Name -descending | select -first 1
# $npm.CreationTime = [DateTime]::Now
# $npm = $npm.ToString()
#}
# find visual studio
# will not work on VSO but VSO does not need it
$vsPath = ""
$vsVer = ""
$msBuild = $null
&$vswhere | foreach {
if ($_.StartsWith("installationPath:")) { $vsPath = $_.SubString("installationPath:".Length).Trim() }
if ($_.StartsWith("installationVersion:")) { $vsVer = $_.SubString("installationVersion:".Length).Trim() }
}
if ($vsPath -ne "")
{
$vsVerParts = $vsVer.Split('.')
$vsMajor = [int]::Parse($vsVerParts[0])
$vsMinor = [int]::Parse($vsVerParts[1])
if ($vsMajor -eq 15) {
$msBuild = "$vsPath\MSBuild\$vsMajor.0\Bin"
}
elseif ($vsMajor -eq 14) {
$msBuild = "c:\Program Files (x86)\MSBuild\$vsMajor\Bin"
}
else
{
$msBuild = $null
}
}
$vs = $null
if ($msBuild)
{
$vs = new-object -typeName PsObject
$vs | add-member -memberType NoteProperty -name Path -value $vsPath
$vs | add-member -memberType NoteProperty -name Major -value $vsMajor
$vs | add-member -memberType NoteProperty -name Minor -value $vsMinor
$vs | add-member -memberType NoteProperty -name MsBuild -value "$msBuild\MsBuild.exe"
}
$solutionRoot = Get-FullPath "$PSScriptRoot\..\..\.."
$uenv = new-object -typeName PsObject
$uenv | add-member -memberType NoteProperty -name SolutionRoot -value $solutionRoot
$uenv | add-member -memberType NoteProperty -name VisualStudio -value $vs
$uenv | add-member -memberType NoteProperty -name NuGet -value $nuget
$uenv | add-member -memberType NoteProperty -name Zip -value $sevenZip
$uenv | add-member -memberType NoteProperty -name VsWhere -value $vswhere
$uenv | add-member -memberType NoteProperty -name Semver -value $semver
$uenv | add-member -memberType NoteProperty -name NodePath -value $node
#$uenv | add-member -memberType NoteProperty -name NpmPath -value $npm
return $uenv
}
@@ -0,0 +1,26 @@
#
# Get-UmbracoVersion
# Gets the Umbraco version
#
function Get-UmbracoVersion
{
$uenv = Get-UmbracoBuildEnv
# parse SolutionInfo and retrieve the version string
$filepath = "$($uenv.SolutionRoot)\src\SolutionInfo.cs"
$text = [System.IO.File]::ReadAllText($filepath)
$match = [System.Text.RegularExpressions.Regex]::Matches($text, "AssemblyInformationalVersion\(`"(.+)?`"\)")
$version = $match.Groups[1]
# semver-parse the version string
$semver = [SemVer.SemVersion]::Parse($version)
$release = "" + $semver.Major + "." + $semver.Minor + "." + $semver.Patch
$versions = new-object -typeName PsObject
$versions | add-member -memberType NoteProperty -name Semver -value $semver
$versions | add-member -memberType NoteProperty -name Release -value $release
$versions | add-member -memberType NoteProperty -name Comment -value $semver.PreRelease
$versions | add-member -memberType NoteProperty -name Build -value $semver.Build
return $versions
}
@@ -0,0 +1,30 @@
# finds msbuild
function Get-VisualStudio($vswhere)
{
$vsPath = ""
$vsVer = ""
&$vswhere | foreach {
if ($_.StartsWith("installationPath:")) { $vsPath = $_.SubString("installationPath:".Length).Trim() }
if ($_.StartsWith("installationVersion:")) { $vsVer = $_.SubString("installationVersion:".Length).Trim() }
}
if ($vsPath -eq "") { return $null }
$vsVerParts = $vsVer.Split('.')
$vsMajor = [int]::Parse($vsVerParts[0])
$vsMinor = [int]::Parse($vsVerParts[1])
if ($vsMajor -eq 15) {
$msBuild = "$vsPath\MSBuild\$vsMajor.$vsMinor\Bin"
}
elseif ($vsMajor -eq 14) {
$msBuild = "c:\Program Files (x86)\MSBuild\$vsMajor\Bin"
}
else { return $null }
$msBuild = "$msBuild\MsBuild.exe"
$vs = new-object -typeName PsObject
$vs | add-member -memberType NoteProperty -name Path -value $vsPath
$vs | add-member -memberType NoteProperty -name Major -value $vsMajor
$vs | add-member -memberType NoteProperty -name Minor -value $vsMinor
$vs | add-member -memberType NoteProperty -name MsBuild -value $msBuild
return $vs
}
@@ -0,0 +1,30 @@
#
# Set-UmbracoContinuousVersion
# Sets the Umbraco version for continuous integration
#
# -Version <version>
# where <version> is a Semver valid version
# eg 1.2.3, 1.2.3-alpha, 1.2.3-alpha+456
#
# -BuildNumber <buildNumber>
# where <buildNumber> is a string coming from the build server
# eg 34, 126, 1
#
function Set-UmbracoContinuousVersion
{
param (
[Parameter(Mandatory=$true)]
[string]
$version,
[Parameter(Mandatory=$true)]
[string]
$buildNumber
)
Write-Host "Version is currently set to $version"
$umbracoVersion = "$($version.Trim())-alpha$($buildNumber)"
Write-Host "Setting Umbraco Version to $umbracoVersion"
Set-UmbracoVersion $umbracoVersion
}
@@ -0,0 +1,117 @@
#
# Set-UmbracoVersion
# Sets the Umbraco version
#
# -Version <version>
# where <version> is a Semver valid version
# eg 1.2.3, 1.2.3-alpha, 1.2.3-alpha+456
#
function Set-UmbracoVersion
{
param (
[Parameter(Mandatory=$true)]
[string]
$version
)
$uenv = Get-UmbracoBuildEnv
try
{
[Reflection.Assembly]::LoadFile($uenv.Semver) > $null
}
catch
{
Write-Error "Failed to load $uenv.Semver"
break
}
# validate input
$ok = [Regex]::Match($version, "^[0-9]+\.[0-9]+\.[0-9]+(\-[a-z0-9]+)?(\+[0-9]+)?$")
if (-not $ok.Success)
{
Write-Error "Invalid version $version"
break
}
# parse input
try
{
$semver = [SemVer.SemVersion]::Parse($version)
}
catch
{
Write-Error "Invalid version $version"
break
}
#
$release = "" + $semver.Major + "." + $semver.Minor + "." + $semver.Patch
# edit files and set the proper versions and dates
Write-Host "Update UmbracoVersion.cs"
Replace-FileText "$($uenv.SolutionRoot)\src\Umbraco.Core\Configuration\UmbracoVersion.cs" `
"(\d+)\.(\d+)\.(\d+)(.(\d+))?" `
"$release"
Replace-FileText "$($uenv.SolutionRoot)\src\Umbraco.Core\Configuration\UmbracoVersion.cs" `
"CurrentComment { get { return `"(.+)`"" `
"CurrentComment { get { return `"$($semver.PreRelease)`""
Write-Host "Update SolutionInfo.cs"
Replace-FileText "$($uenv.SolutionRoot)\src\SolutionInfo.cs" `
"AssemblyFileVersion\(`"(.+)?`"\)" `
"AssemblyFileVersion(`"$release`")"
Replace-FileText "$($uenv.SolutionRoot)\src\SolutionInfo.cs" `
"AssemblyInformationalVersion\(`"(.+)?`"\)" `
"AssemblyInformationalVersion(`"$semver`")"
$year = [System.DateTime]::Now.ToString("yyyy")
Replace-FileText "$($uenv.SolutionRoot)\src\SolutionInfo.cs" `
"AssemblyCopyright\(`"Copyright © Umbraco (\d{4})`"\)" `
"AssemblyCopyright(`"Copyright © Umbraco $year`")"
# edit csproj and set IIS Express port number
# this is a raw copy of ReplaceIISExpressPortNumber.exe
# it probably can be achieved in a much nicer way - l8tr
$source = @"
using System;
using System.IO;
using System.Xml;
using System.Globalization;
namespace Umbraco
{
public static class PortUpdater
{
public static void Update(string path, string release)
{
XmlDocument xmlDocument = new XmlDocument();
string fullPath = Path.GetFullPath(path);
xmlDocument.Load(fullPath);
int result = 1;
int.TryParse(release.Replace(`".`", `"`"), out result);
while (result < 1024)
result *= 10;
XmlNode xmlNode1 = xmlDocument.GetElementsByTagName(`"IISUrl`").Item(0);
if (xmlNode1 != null)
xmlNode1.InnerText = `"http://localhost:`" + (object) result;
XmlNode xmlNode2 = xmlDocument.GetElementsByTagName(`"DevelopmentServerPort`").Item(0);
if (xmlNode2 != null)
xmlNode2.InnerText = result.ToString((IFormatProvider) CultureInfo.InvariantCulture);
xmlDocument.Save(fullPath);
}
}
}
"@
$assem = (
"System.Xml",
"System.IO",
"System.Globalization"
)
Write-Host "Update Umbraco.Web.UI.csproj"
add-type -referencedAssemblies $assem -typeDefinition $source -language CSharp
$csproj = "$($uenv.SolutionRoot)\src\Umbraco.Web.UI\Umbraco.Web.UI.csproj"
[Umbraco.PortUpdater]::Update($csproj, $release)
return $semver
}
@@ -0,0 +1,605 @@
# Umbraco.Build.psm1
#
# $env:PSModulePath = "$pwd\build\Modules\;$env:PSModulePath"
# Import-Module Umbraco.Build -Force -DisableNameChecking
#
# PowerShell Modules:
# https://msdn.microsoft.com/en-us/library/dd878324%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
#
# PowerShell Module Manifest:
# https://msdn.microsoft.com/en-us/library/dd878337%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
#
# See also
# http://www.powershellmagazine.com/2014/08/15/pstip-taking-control-of-verbose-and-debug-output-part-5/
. "$PSScriptRoot\Utilities.ps1"
. "$PSScriptRoot\Get-VisualStudio.ps1"
. "$PSScriptRoot\Get-UmbracoBuildEnv.ps1"
. "$PSScriptRoot\Set-UmbracoVersion.ps1"
. "$PSScriptRoot\Set-UmbracoContinuousVersion.ps1"
. "$PSScriptRoot\Get-UmbracoVersion.ps1"
. "$PSScriptRoot\Verify-NuGet.ps1"
. "$PSScriptRoot\Build-UmbracoDocs.ps1"
#
# Prepares the build
#
function Prepare-Build
{
param (
$uenv, # an Umbraco build environment (see Get-UmbracoBuildEnv)
[Alias("k")]
[switch]
$keep = $false
)
Write-Host ">> Prepare Build"
$src = "$($uenv.SolutionRoot)\src"
$tmp = "$($uenv.SolutionRoot)\build.tmp"
$out = "$($uenv.SolutionRoot)\build.out"
# clear
Write-Host "Clear folders and files"
Remove-Directory "$src\Umbraco.Web.UI.Client\bower_components"
if (-not $keep)
{
Remove-Directory "$tmp"
mkdir "$tmp" > $null
Remove-Directory "$out"
mkdir "$out" > $null
}
# ensure proper web.config
$webUi = "$src\Umbraco.Web.UI"
Store-WebConfig $webUi
Write-Host "Create clean web.config"
Copy-File "$webUi\web.Template.config" "$webUi\web.config"
}
function Clear-EnvVar($var)
{
$value = [Environment]::GetEnvironmentVariable($var)
if (test-path "env:$var") { rm "env:$var" }
return $value
}
function Set-EnvVar($var, $value)
{
if ($value)
{
[Environment]::SetEnvironmentVariable($var, $value)
}
else
{
if (test-path "env:$var") { rm "env:$var" }
}
}
function Sandbox-Node
{
param (
$uenv # an Umbraco build environment (see Get-UmbracoBuildEnv)
)
$global:node_path = $env:path
$nodePath = $uenv.NodePath
$gitExe = (get-command git).Source
$gitPath = [System.IO.Path]::GetDirectoryName($gitExe)
$env:path = "$nodePath;$gitPath"
$global:node_nodepath = Clear-EnvVar "NODEPATH"
$global:node_npmcache = Clear-EnvVar "NPM_CONFIG_CACHE"
$global:node_npmprefix = Clear-EnvVar "NPM_CONFIG_PREFIX"
}
function Restore-Node
{
$env:path = $node_path
Set-EnvVar "NODEPATH" $node_nodepath
Set-EnvVar "NPM_CONFIG_CACHE" $node_npmcache
Set-EnvVar "NPM_CONFIG_PREFIX" $node_npmprefix
}
#
# Builds the Belle UI project
#
function Compile-Belle
{
param (
$uenv, # an Umbraco build environment (see Get-UmbracoBuildEnv)
$version # an Umbraco version object (see Get-UmbracoVersion)
)
$tmp = "$($uenv.SolutionRoot)\build.tmp"
$src = "$($uenv.SolutionRoot)\src"
Write-Host ">> Compile Belle"
Write-Host "Logging to $tmp\belle.log"
# get a temp clean node env (will restore)
Sandbox-Node $uenv
push-location "$($uenv.SolutionRoot)\src\Umbraco.Web.UI.Client"
write "node version is:" > $tmp\belle.log
&node -v >> $tmp\belle.log 2>&1
write "npm version is:" >> $tmp\belle.log 2>&1
&npm -v >> $tmp\belle.log 2>&1
write "cleaning npm cache" >> $tmp\belle.log 2>&1
&npm cache clean >> $tmp\belle.log 2>&1
write "installing bower" >> $tmp\belle.log 2>&1
&npm install -g bower >> $tmp\belle.log 2>&1
write "installing gulp" >> $tmp\belle.log 2>&1
&npm install -g gulp >> $tmp\belle.log 2>&1
write "installing gulp-cli" >> $tmp\belle.log 2>&1
&npm install -g gulp-cli --quiet >> $tmp\belle.log 2>&1
write "executing npm install" >> $tmp\belle.log 2>&1
&npm install >> $tmp\belle.log 2>&1
write "executing gulp build for version $version" >> $tmp\belle.log 2>&1
&gulp build --buildversion=$version.Release >> $tmp\belle.log 2>&1
pop-location
# fixme - should we filter the log to find errors?
#get-content .\build.tmp\belle.log | %{ if ($_ -match "build") { write $_}}
# restore
Restore-Node
# setting node_modules folder to hidden
# used to prevent VS13 from crashing on it while loading the websites project
# also makes sure aspnet compiler does not try to handle rogue files and chokes
# in VSO with Microsoft.VisualC.CppCodeProvider -related errors
# use get-item -force 'cos it might be hidden already
write "Set hidden attribute on node_modules"
$dir = get-item -force "$src\Umbraco.Web.UI.Client\node_modules"
$dir.Attributes = $dir.Attributes -bor ([System.IO.FileAttributes]::Hidden)
}
#
# Compiles Umbraco
#
function Compile-Umbraco
{
param (
$uenv, # an Umbraco build environment (see Get-UmbracoBuildEnv)
[string] $buildConfiguration = "Release"
)
$src = "$($uenv.SolutionRoot)\src"
$tmp = "$($uenv.SolutionRoot)\build.tmp"
$out = "$($uenv.SolutionRoot)\build.out"
if ($uenv.VisualStudio -eq $null)
{
Write-Error "Build environment does not provide VisualStudio."
break
}
$toolsVersion = "4.0"
if ($uenv.VisualStudio.Major -eq 15)
{
$toolsVersion = "15.0"
}
Write-Host ">> Compile Umbraco"
Write-Host "Logging to $tmp\msbuild.umbraco.log"
# beware of the weird double \\ at the end of paths
# see http://edgylogic.com/blog/powershell-and-external-commands-done-right/
&$uenv.VisualStudio.MsBuild "$src\Umbraco.Web.UI\Umbraco.Web.UI.csproj" `
/p:WarningLevel=0 `
/p:Configuration=$buildConfiguration `
/p:Platform=AnyCPU `
/p:UseWPP_CopyWebApplication=True `
/p:PipelineDependsOnBuild=False `
/p:OutDir=$tmp\bin\\ `
/p:WebProjectOutputDir=$tmp\WebApp\\ `
/p:Verbosity=minimal `
/t:Clean`;Rebuild `
/tv:$toolsVersion `
/p:UmbracoBuild=True `
> $tmp\msbuild.umbraco.log
# /p:UmbracoBuild tells the csproj that we are building from PS
}
#
# Prepare Tests
#
function Prepare-Tests
{
param (
$uenv # an Umbraco build environment (see Get-UmbracoBuildEnv)
)
$src = "$($uenv.SolutionRoot)\src"
$tmp = "$($uenv.SolutionRoot)\build.tmp"
Write-Host ">> Prepare Tests"
# fixme - idea is to avoid rebuilding everything for tests
# but because of our weird assembly versioning (with .* stuff)
# everything gets rebuilt all the time...
#Copy-Files "$tmp\bin" "." "$tmp\tests"
# data
Write-Host "Copy data files"
if( -Not (Test-Path -Path "$tmp\tests\Packaging" ) )
{
Write-Host "Create packaging directory"
New-Item -ItemType directory -Path "$tmp\tests\Packaging"
}
Copy-Files "$src\Umbraco.Tests\Packaging\Packages" "*" "$tmp\tests\Packaging\Packages"
# required for package install tests
if( -Not (Test-Path -Path "$tmp\tests\bin" ) )
{
Write-Host "Create bin directory"
New-Item -ItemType directory -Path "$tmp\tests\bin"
}
}
#
# Compiles Tests
#
function Compile-Tests
{
param (
$uenv # an Umbraco build environment (see Get-UmbracoBuildEnv)
)
$src = "$($uenv.SolutionRoot)\src"
$tmp = "$($uenv.SolutionRoot)\build.tmp"
$out = "$tmp\tests"
$buildConfiguration = "Release"
if ($uenv.VisualStudio -eq $null)
{
Write-Error "Build environment does not provide VisualStudio."
break
}
$toolsVersion = "4.0"
if ($uenv.VisualStudio.Major -eq 15)
{
$toolsVersion = "15.0"
}
Write-Host ">> Compile Tests"
Write-Host "Logging to $tmp\msbuild.tests.log"
# beware of the weird double \\ at the end of paths
# see http://edgylogic.com/blog/powershell-and-external-commands-done-right/
&$uenv.VisualStudio.MsBuild "$src\Umbraco.Tests\Umbraco.Tests.csproj" `
/p:WarningLevel=0 `
/p:Configuration=$buildConfiguration `
/p:Platform=AnyCPU `
/p:UseWPP_CopyWebApplication=True `
/p:PipelineDependsOnBuild=False `
/p:OutDir=$out\\ `
/p:Verbosity=minimal `
/t:Build `
/tv:$toolsVersion `
/p:UmbracoBuild=True `
/p:NugetPackages=$src\packages `
> $tmp\msbuild.tests.log
# /p:UmbracoBuild tells the csproj that we are building from PS
}
#
# Cleans things up and prepare files after compilation
#
function Prepare-Packages
{
param (
$uenv # an Umbraco build environment (see Get-UmbracoBuildEnv)
)
Write-Host ">> Prepare Packages"
$src = "$($uenv.SolutionRoot)\src"
$tmp = "$($uenv.SolutionRoot)\build.tmp"
$out = "$($uenv.SolutionRoot)\build.out"
$buildConfiguration = "Release"
# restore web.config
Restore-WebConfig "$src\Umbraco.Web.UI"
# cleanup build
Write-Host "Clean build"
Remove-File "$tmp\bin\*.dll.config"
Remove-File "$tmp\WebApp\bin\*.dll.config"
# cleanup presentation
Write-Host "Cleanup presentation"
Remove-Directory "$tmp\WebApp\umbraco.presentation"
# create directories
Write-Host "Create directories"
mkdir "$tmp\Configs" > $null
mkdir "$tmp\Configs\Lang" > $null
mkdir "$tmp\WebApp\App_Data" > $null
#mkdir "$tmp\WebApp\Media" > $null
#mkdir "$tmp\WebApp\Views" > $null
# copy various files
Write-Host "Copy xml documentation"
cp -force "$tmp\bin\*.xml" "$tmp\WebApp\bin"
Write-Host "Copy transformed configs and langs"
# note: exclude imageprocessor/*.config as imageprocessor pkg installs them
Copy-Files "$tmp\WebApp\config" "*.config" "$tmp\Configs" `
{ -not $_.RelativeName.StartsWith("imageprocessor") }
Copy-Files "$tmp\WebApp\config" "*.js" "$tmp\Configs"
Copy-Files "$tmp\WebApp\config\lang" "*.xml" "$tmp\Configs\Lang"
Copy-File "$tmp\WebApp\web.config" "$tmp\Configs\web.config.transform"
Write-Host "Copy transformed web.config"
Copy-File "$src\Umbraco.Web.UI\web.$buildConfiguration.Config.transformed" "$tmp\WebApp\web.config"
# offset the modified timestamps on all umbraco dlls, as WebResources
# break if date is in the future, which, due to timezone offsets can happen.
Write-Host "Offset dlls timestamps"
ls -r "$tmp\*.dll" | foreach {
$_.CreationTime = $_.CreationTime.AddHours(-11)
$_.LastWriteTime = $_.LastWriteTime.AddHours(-11)
}
# copy libs
Write-Host "Copy SqlCE libraries"
Copy-Files "$src\packages\SqlServerCE.4.0.0.1" "*.*" "$tmp\bin" `
{ -not $_.Extension.StartsWith(".nu") -and -not $_.RelativeName.StartsWith("lib\") }
Copy-Files "$src\packages\SqlServerCE.4.0.0.1" "*.*" "$tmp\WebApp\bin" `
{ -not $_.Extension.StartsWith(".nu") -and -not $_.RelativeName.StartsWith("lib\") }
# copy Belle
Write-Host "Copy Belle"
Copy-Files "$src\Umbraco.Web.UI\umbraco\assets" "*" "$tmp\WebApp\umbraco\assets"
Copy-Files "$src\Umbraco.Web.UI\umbraco\js" "*" "$tmp\WebApp\umbraco\js"
Copy-Files "$src\Umbraco.Web.UI\umbraco\lib" "*" "$tmp\WebApp\umbraco\lib"
Copy-Files "$src\Umbraco.Web.UI\umbraco\views" "*" "$tmp\WebApp\umbraco\views"
Copy-Files "$src\Umbraco.Web.UI\umbraco\preview" "*" "$tmp\WebApp\umbraco\preview"
# prepare WebPI
Write-Host "Prepare WebPI"
Remove-Directory "$tmp\WebPi"
mkdir "$tmp\WebPi" > $null
mkdir "$tmp\WebPi\umbraco" > $null
Copy-Files "$tmp\WebApp" "*" "$tmp\WebPi\umbraco"
Copy-Files "$src\WebPi" "*" "$tmp\WebPi"
}
#
# Creates the Zip packages
#
function Package-Zip
{
param (
$uenv # an Umbraco build environment (see Get-UmbracoBuildEnv)
)
Write-Host ">> Create Zip packages"
$src = "$($uenv.SolutionRoot)\src"
$tmp = "$($uenv.SolutionRoot)\build.tmp"
$out = "$($uenv.SolutionRoot)\build.out"
Write-Host "Zip all binaries"
&$uenv.Zip a -r "$out\UmbracoCms.AllBinaries.$($version.Semver).zip" `
"$tmp\bin\*" `
"-x!dotless.Core.*" `
> $null
Write-Host "Zip cms"
&$uenv.Zip a -r "$out\UmbracoCms.$($version.Semver).zip" `
"$tmp\WebApp\*" `
"-x!dotless.Core.*" "-x!Content_Types.xml" "-x!*.pdb"`
> $null
Write-Host "Zip WebPI"
&$uenv.Zip a -r "$out\UmbracoCms.WebPI.$($version.Semver).zip" "-x!*.pdb"`
"$tmp\WebPi\*" `
"-x!dotless.Core.*" `
> $null
# hash the webpi file
Write-Host "Hash WebPI"
$hash = Get-FileHash "$out\UmbracoCms.WebPI.$($version.Semver).zip"
Write $hash | out-file "$out\webpihash.txt" -encoding ascii
}
#
# Prepares NuGet
#
function Prepare-NuGet
{
param (
$uenv # an Umbraco build environment (see Get-UmbracoBuildEnv)
)
Write-Host ">> Prepare NuGet"
$src = "$($uenv.SolutionRoot)\src"
$tmp = "$($uenv.SolutionRoot)\build.tmp"
$out = "$($uenv.SolutionRoot)\build.out"
# add Web.config transform files to the NuGet package
Write-Host "Add web.config transforms to NuGet package"
mv "$tmp\WebApp\Views\Web.config" "$tmp\WebApp\Views\Web.config.transform"
# fixme - that one does not exist in .bat build either?
#mv "$tmp\WebApp\Xslt\Web.config" "$tmp\WebApp\Xslt\Web.config.transform"
}
#
# Restores NuGet
#
function Restore-NuGet
{
param (
$uenv # an Umbraco build environment (see Get-UmbracoBuildEnv)
)
$src = "$($uenv.SolutionRoot)\src"
$tmp = "$($uenv.SolutionRoot)\build.tmp"
Write-Host ">> Restore NuGet"
Write-Host "Logging to $tmp\nuget.restore.log"
&$uenv.NuGet restore "$src\Umbraco.sln" > "$tmp\nuget.restore.log"
}
#
# Creates the NuGet packages
#
function Package-NuGet
{
param (
$uenv, # an Umbraco build environment (see Get-UmbracoBuildEnv)
$version # an Umbraco version object (see Get-UmbracoVersion)
)
$src = "$($uenv.SolutionRoot)\src"
$tmp = "$($uenv.SolutionRoot)\build.tmp"
$out = "$($uenv.SolutionRoot)\build.out"
$nuspecs = "$($uenv.SolutionRoot)\build\NuSpecs"
Write-Host ">> Create NuGet packages"
# see https://docs.microsoft.com/en-us/nuget/schema/nuspec
# note - warnings about SqlCE native libs being outside of 'lib' folder,
# nothing much we can do about it as it's intentional yet there does not
# seem to be a way to disable the warning
&$uenv.NuGet Pack "$nuspecs\UmbracoCms.Core.nuspec" `
-Properties BuildTmp="$tmp" `
-Version $version.Semver.ToString() `
-Symbols -Verbosity quiet -outputDirectory $out
&$uenv.NuGet Pack "$nuspecs\UmbracoCms.nuspec" `
-Properties BuildTmp="$tmp" `
-Version $version.Semver.ToString() `
-Verbosity quiet -outputDirectory $out
}
#
# Builds Umbraco
#
function Build-Umbraco
{
[CmdletBinding()]
param (
[string]
$target = "all",
[string]
$buildConfiguration = "Release"
)
$target = $target.ToLowerInvariant()
Write-Host ">> Build-Umbraco <$target> <$buildConfiguration>"
Write-Host "Get Build Environment"
$uenv = Get-UmbracoBuildEnv
Write-Host "Get Version"
$version = Get-UmbracoVersion
Write-Host "Version $($version.Semver)"
if ($target -eq "pre-build")
{
Prepare-Build $uenv
#Compile-Belle $uenv $version
# set environment variables
$env:UMBRACO_VERSION=$version.Semver.ToString()
$env:UMBRACO_RELEASE=$version.Release
$env:UMBRACO_COMMENT=$version.Comment
$env:UMBRACO_BUILD=$version.Build
# set environment variable for VSO
# https://github.com/Microsoft/vsts-tasks/issues/375
# https://github.com/Microsoft/vsts-tasks/blob/master/docs/authoring/commands.md
Write-Host ("##vso[task.setvariable variable=UMBRACO_VERSION;]$($version.Semver.ToString())")
Write-Host ("##vso[task.setvariable variable=UMBRACO_RELEASE;]$($version.Release)")
Write-Host ("##vso[task.setvariable variable=UMBRACO_COMMENT;]$($version.Comment)")
Write-Host ("##vso[task.setvariable variable=UMBRACO_BUILD;]$($version.Build)")
Write-Host ("##vso[task.setvariable variable=UMBRACO_TMP;]$($uenv.SolutionRoot)\build.tmp")
}
elseif ($target -eq "pre-tests")
{
Prepare-Tests $uenv
}
elseif ($target -eq "compile-tests")
{
Compile-Tests $uenv
}
elseif ($target -eq "compile-umbraco")
{
Compile-Umbraco $uenv $buildConfiguration
}
elseif ($target -eq "pre-packages")
{
Prepare-Packages $uenv
}
elseif ($target -eq "pre-nuget")
{
Prepare-NuGet $uenv
}
elseif ($target -eq "restore-nuget")
{
Restore-NuGet $uenv
}
elseif ($target -eq "pkg-zip")
{
Package-Zip $uenv
}
elseif ($target -eq "compile-belle")
{
Compile-Belle $uenv $version
}
elseif ($target -eq "all")
{
Prepare-Build $uenv
Restore-NuGet $uenv
Compile-Belle $uenv $version
Compile-Umbraco $uenv $buildConfiguration
Prepare-Tests $uenv
Compile-Tests $uenv
# not running tests...
Prepare-Packages $uenv
Package-Zip $uenv
Verify-NuGet $uenv
Prepare-NuGet $uenv
Package-NuGet $uenv $version
}
else
{
Write-Error "Unsupported target `"$target`"."
}
}
#
# export functions
#
Export-ModuleMember -function Get-UmbracoBuildEnv
Export-ModuleMember -function Set-UmbracoVersion
Export-ModuleMember -function Set-UmbracoContinuousVersion
Export-ModuleMember -function Get-UmbracoVersion
Export-ModuleMember -function Build-Umbraco
Export-ModuleMember -function Build-UmbracoDocs
Export-ModuleMember -function Verify-NuGet
#eof
+120
View File
@@ -0,0 +1,120 @@
# returns a string containing the hash of $file
function Get-FileHash($file)
{
try
{
$crypto = new-object System.Security.Cryptography.SHA1CryptoServiceProvider
$stream = [System.IO.File]::OpenRead($file)
$hash = $crypto.ComputeHash($stream)
$text = ""
$hash | foreach `
{
$text = $text + $_.ToString("x2")
}
return $text
}
finally
{
if ($stream)
{
$stream.Dispose()
}
$crypto.Dispose()
}
}
# returns the full path if $file is relative to $pwd
function Get-FullPath($file)
{
$path = [System.IO.Path]::Combine($pwd, $file)
$path = [System.IO.Path]::GetFullPath($path)
return $path
}
# removes a directory, doesn't complain if it does not exist
function Remove-Directory($dir)
{
remove-item $dir -force -recurse -errorAction SilentlyContinue > $null
}
# removes a file, doesn't complain if it does not exist
function Remove-File($file)
{
remove-item $file -force -errorAction SilentlyContinue > $null
}
# copies a file, creates target dir if needed
function Copy-File($source, $target)
{
$ignore = new-item -itemType file -path $target -force
cp -force $source $target
}
# copies files to a directory
function Copy-Files($source, $select, $target, $filter)
{
$files = ls -r "$source\$select"
$files | foreach {
$relative = $_.FullName.SubString($source.Length+1)
$_ | add-member -memberType NoteProperty -name RelativeName -value $relative
}
if ($filter -ne $null) {
$files = $files | where $filter
}
$files |
foreach {
if ($_.PsIsContainer) {
$ignore = new-item -itemType directory -path "$target\$($_.RelativeName)" -force
}
else {
Copy-File $_.FullName "$target\$($_.RelativeName)"
}
}
}
# regex-replaces content in a file
function Replace-FileText($filename, $source, $replacement)
{
$filepath = Get-FullPath $filename
$text = [System.IO.File]::ReadAllText($filepath)
$text = [System.Text.RegularExpressions.Regex]::Replace($text, $source, $replacement)
$utf8bom = New-Object System.Text.UTF8Encoding $true
[System.IO.File]::WriteAllText($filepath, $text, $utf8bom)
}
# store web.config
function Store-WebConfig($webUi)
{
if (test-path "$webUi\web.config")
{
if (test-path "$webUi\web.config.temp-build")
{
Write-Host "Found existing web.config.temp-build"
$i = 0
while (test-path "$webUi\web.config.temp-build.$i")
{
$i = $i + 1
}
Write-Host "Save existing web.config as web.config.temp-build.$i"
Write-Host "(WARN: the original web.config.temp-build will be restored during post-build)"
mv "$webUi\web.config" "$webUi\web.config.temp-build.$i"
}
else
{
Write-Host "Save existing web.config as web.config.temp-build"
Write-Host "(will be restored during post-build)"
mv "$webUi\web.config" "$webUi\web.config.temp-build"
}
}
}
# restore web.config
function Restore-WebConfig($webUi)
{
if (test-path "$webUi\web.config.temp-build")
{
Write-Host "Restoring existing web.config"
Remove-File "$webUi\web.config"
mv "$webUi\web.config.temp-build" "$webUi\web.config"
}
}
@@ -0,0 +1,444 @@
#
# Verify-NuGet
#
function Format-Dependency
{
param ( $d )
$m = $d.Id + " "
if ($d.MinInclude) { $m = $m + "[" }
else { $m = $m + "(" }
$m = $m + $d.MinVersion
if ($d.MaxVersion -ne $d.MinVersion) { $m = $m + "," + $d.MaxVersion }
if ($d.MaxInclude) { $m = $m + "]" }
else { $m = $m + ")" }
return $m
}
function Write-NuSpec
{
param ( $name, $deps )
Write-Host ""
Write-Host "$name NuSpec dependencies:"
foreach ($d in $deps)
{
$m = Format-Dependency $d
Write-Host " $m"
}
}
function Write-Package
{
param ( $name, $pkgs )
Write-Host ""
Write-Host "$name packages:"
foreach ($p in $pkgs)
{
Write-Host " $($p.Id) $($p.Version)"
}
}
function Verify-NuGet
{
param (
$uenv # an Umbraco build environment (see Get-UmbracoBuildEnv)
)
if ($uenv -eq $null)
{
$uenv = Get-UmbracoBuildEnv
}
$source = @"
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using Semver;
namespace Umbraco.Build
{
public class NuGet
{
public static Dependency[] GetNuSpecDependencies(string filename)
{
NuSpec nuspec;
var serializer = new XmlSerializer(typeof(NuSpec));
using (var reader = new StreamReader(filename))
{
nuspec = (NuSpec) serializer.Deserialize(reader);
}
var nudeps = nuspec.Metadata.Dependencies;
var deps = new List<Dependency>();
foreach (var nudep in nudeps)
{
var dep = new Dependency();
dep.Id = nudep.Id;
var parts = nudep.Version.Split(',');
if (parts.Length == 1)
{
dep.MinInclude = parts[0].StartsWith("[");
dep.MaxInclude = parts[0].EndsWith("]");
SemVersion version;
if (!SemVersion.TryParse(parts[0].Substring(1, parts[0].Length-2).Trim(), out version)) continue;
dep.MinVersion = dep.MaxVersion = version; //parts[0].Substring(1, parts[0].Length-2).Trim();
}
else
{
SemVersion version;
if (!SemVersion.TryParse(parts[0].Substring(1).Trim(), out version)) continue;
dep.MinVersion = version; //parts[0].Substring(1).Trim();
if (!SemVersion.TryParse(parts[1].Substring(0, parts[1].Length-1).Trim(), out version)) continue;
dep.MaxVersion = version; //parts[1].Substring(0, parts[1].Length-1).Trim();
dep.MinInclude = parts[0].StartsWith("[");
dep.MaxInclude = parts[1].EndsWith("]");
}
deps.Add(dep);
}
return deps.ToArray();
}
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(/*this*/ IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
HashSet<TKey> knownKeys = new HashSet<TKey>();
foreach (TSource element in source)
{
if (knownKeys.Add(keySelector(element)))
{
yield return element;
}
}
}
public static Package[] GetProjectsPackages(string src, string[] projects)
{
var l = new List<Package>();
foreach (var project in projects)
{
var path = Path.Combine(src, project);
var packageConfig = Path.Combine(path, "packages.config");
if (File.Exists(packageConfig))
ReadPackagesConfig(packageConfig, l);
var csprojs = Directory.GetFiles(path, "*.csproj");
foreach (var csproj in csprojs)
{
ReadCsProj(csproj, l);
}
}
IEnumerable<Package> p = l.OrderBy(x => x.Id);
p = DistinctBy(p, x => x.Id + ":::" + x.Version);
return p.ToArray();
}
public static object[] GetPackageErrors(Package[] pkgs)
{
return pkgs
.GroupBy(x => x.Id)
.Where(x => x.Count() > 1)
.ToArray();
}
public static object[] GetNuSpecErrors(Package[] pkgs, Dependency[] deps)
{
var d = pkgs.ToDictionary(x => x.Id, x => x.Version);
return deps
.Select(x =>
{
SemVersion v;
if (!d.TryGetValue(x.Id, out v)) return null;
var ok = true;
/*
if (x.MinInclude)
{
if (v < x.MinVersion) ok = false;
}
else
{
if (v <= x.MinVersion) ok = false;
}
if (x.MaxInclude)
{
if (v > x.MaxVersion) ok = false;
}
else
{
if (v >= x.MaxVersion) ok = false;
}
*/
if (!x.MinInclude || v != x.MinVersion) ok = false;
return ok ? null : new { Dependency = x, Version = v };
})
.Where(x => x != null)
.ToArray();
}
/*
public static Package[] GetProjectPackages(string path)
{
var l = new List<Package>();
var packageConfig = Path.Combine(path, "packages.config");
if (File.Exists(packageConfig))
ReadPackagesConfig(packageConfig, l);
var csprojs = Directory.GetFiles(path, "*.csproj");
foreach (var csproj in csprojs)
{
ReadCsProj(csproj, l);
}
return l.ToArray();
}
*/
public static string GetDirectoryName(string filename)
{
return Path.GetFileName(Path.GetDirectoryName(filename));
}
public static void ReadPackagesConfig(string filename, List<Package> packages)
{
//Console.WriteLine("read " + filename);
PackagesConfigPackages pkgs;
var serializer = new XmlSerializer(typeof(PackagesConfigPackages));
using (var reader = new StreamReader(filename))
{
pkgs = (PackagesConfigPackages) serializer.Deserialize(reader);
}
foreach (var p in pkgs.Packages)
{
SemVersion version;
if (!SemVersion.TryParse(p.Version, out version)) continue;
packages.Add(new Package { Id = p.Id, Version = version, Project = GetDirectoryName(filename) });
}
}
public static void ReadCsProj(string filename, List<Package> packages)
{
//Console.WriteLine("read " + filename);
// if xmlns then it's not a VS2017 with PackageReference
var text = File.ReadAllLines(filename);
var line = text.FirstOrDefault(x => x.Contains("<Project"));
if (line == null) return;
if (line.Contains("xmlns")) return;
CsProjProject proj;
var serializer = new XmlSerializer(typeof(CsProjProject));
using (var reader = new StreamReader(filename))
{
proj = (CsProjProject) serializer.Deserialize(reader);
}
foreach (var p in proj.ItemGroups.Where(x => x.Packages != null).SelectMany(x => x.Packages))
{
var sversion = p.VersionE ?? p.VersionA;
SemVersion version;
if (!SemVersion.TryParse(sversion, out version)) continue;
packages.Add(new Package { Id = p.Id, Version = version, Project = GetDirectoryName(filename) });
}
}
public class Dependency
{
public string Id { get; set; }
public SemVersion MinVersion { get; set; }
public SemVersion MaxVersion { get; set; }
public bool MinInclude { get; set; }
public bool MaxInclude { get; set; }
}
public class Package
{
public string Id { get; set; }
public SemVersion Version { get; set; }
public string Project { get; set; }
}
[XmlType(AnonymousType = true, Namespace = "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd")]
[XmlRoot(Namespace = "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd", IsNullable = false, ElementName = "package")]
public class NuSpec
{
[XmlElement("metadata")]
public NuSpecMetadata Metadata { get; set; }
}
[XmlType(AnonymousType = true, Namespace = "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd", TypeName = "metadata")]
public class NuSpecMetadata
{
[XmlArray("dependencies")]
[XmlArrayItem("dependency", IsNullable = false)]
public NuSpecDependency[] Dependencies { get; set; }
}
[XmlType(AnonymousType = true, Namespace = "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd", TypeName = "dependencies")]
public class NuSpecDependency
{
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
[XmlAttribute(AttributeName = "version")]
public string Version { get; set; }
}
[XmlType(AnonymousType = true)]
[XmlRoot(Namespace = "", IsNullable = false, ElementName = "packages")]
public class PackagesConfigPackages
{
[XmlElement("package")]
public PackagesConfigPackage[] Packages { get; set; }
}
[XmlType(AnonymousType = true, TypeName = "package")]
public class PackagesConfigPackage
{
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
[XmlAttribute(AttributeName = "version")]
public string Version { get; set; }
}
[XmlType(AnonymousType = true)]
[XmlRoot(Namespace = "", IsNullable = false, ElementName = "Project")]
public class CsProjProject
{
[XmlElement("ItemGroup")]
public CsProjItemGroup[] ItemGroups { get; set; }
}
[XmlType(AnonymousType = true, TypeName = "ItemGroup")]
public class CsProjItemGroup
{
[XmlElement("PackageReference")]
public CsProjPackageReference[] Packages { get; set; }
}
[XmlType(AnonymousType = true, TypeName = "PackageReference")]
public class CsProjPackageReference
{
[XmlAttribute(AttributeName = "Include")]
public string Id { get; set; }
[XmlAttribute(AttributeName = "Version")]
public string VersionA { get; set; }
[XmlElement("Version")]
public string VersionE { get; set;}
}
}
}
"@
Write-Host ">> Verify NuGet consistency"
$assem = (
"System.Xml",
"System.Core", # "System.Collections.Generic"
"System.Linq",
"System.Xml.Serialization",
"System.IO",
"System.Globalization",
$uenv.Semver
)
try
{
# as long as the code hasn't changed it's fine to re-add, but if the code
# has changed this will throw - better warn the dev that we have an issue
add-type -referencedAssemblies $assem -typeDefinition $source -language CSharp
}
catch
{
if ($_.FullyQualifiedErrorId.StartsWith("TYPE_ALREADY_EXISTS,"))
{ Write-Error "Failed to add type, did you change the code?" }
else
{ Write-Error $_ }
}
if (-not $?) { break }
$nuspecs = (
"UmbracoCms",
"UmbracoCms.Core"
)
$projects = (
"Umbraco.Core",
"Umbraco.Web",
"Umbraco.Web.UI",
"UmbracoExamine"#,
#"Umbraco.Tests",
#"Umbraco.Tests.Benchmarks"
)
$src = "$($uenv.SolutionRoot)\src"
$pkgs = [Umbraco.Build.NuGet]::GetProjectsPackages($src, $projects)
if (-not $?) { break }
#Write-Package "All" $pkgs
$errs = [Umbraco.Build.NuGet]::GetPackageErrors($pkgs)
if (-not $?) { break }
if ($errs.Length -gt 0)
{
Write-Host ""
}
foreach ($err in $errs)
{
Write-Host $err.Key
foreach ($e in $err)
{
Write-Host " $($e.Version) required by $($e.Project)"
}
}
if ($errs.Length -gt 0)
{
Write-Error "Found non-consolidated package dependencies"
break
}
$nuerr = $false
$nupath = "$($uenv.SolutionRoot)\build\NuSpecs"
foreach ($nuspec in $nuspecs)
{
$deps = [Umbraco.Build.NuGet]::GetNuSpecDependencies("$nupath\$nuspec.nuspec")
if (-not $?) { break }
#Write-NuSpec $nuspec $deps
$errs = [Umbraco.Build.NuGet]::GetNuSpecErrors($pkgs, $deps)
if (-not $?) { break }
if ($errs.Length -gt 0)
{
Write-Host ""
Write-Host "$nuspec requires:"
$nuerr = $true
}
foreach ($err in $errs)
{
$m = Format-Dependency $err.Dependency
Write-Host " $m but projects require $($err.Version)"
}
}
if ($nuerr)
{
Write-Error "Found inconsistent NuGet dependencies"
break
}
}
+44 -44
View File
@@ -44,65 +44,65 @@
</dependencies>
</metadata>
<files>
<file src="..\_BuildOutput\WebApp\bin\businesslogic.dll" target="lib\net45\businesslogic.dll" />
<file src="..\_BuildOutput\WebApp\bin\businesslogic.xml" target="lib\net45\businesslogic.xml" />
<file src="..\_BuildOutput\WebApp\bin\cms.dll" target="lib\net45\cms.dll" />
<file src="..\_BuildOutput\WebApp\bin\cms.xml" target="lib\net45\cms.xml" />
<file src="..\_BuildOutput\WebApp\bin\controls.dll" target="lib\net45\controls.dll" />
<file src="..\_BuildOutput\WebApp\bin\controls.xml" target="lib\net45\controls.xml" />
<file src="..\_BuildOutput\WebApp\bin\interfaces.dll" target="lib\net45\interfaces.dll" />
<file src="..\_BuildOutput\WebApp\bin\interfaces.xml" target="lib\net45\interfaces.xml" />
<file src="..\_BuildOutput\WebApp\bin\log4net.dll" target="lib\net45\log4net.dll" />
<file src="..\_BuildOutput\WebApp\bin\Microsoft.ApplicationBlocks.Data.dll" target="lib\net45\Microsoft.ApplicationBlocks.Data.dll" />
<file src="..\_BuildOutput\WebApp\bin\SQLCE4Umbraco.dll" target="lib\net45\SQLCE4Umbraco.dll" />
<file src="..\_BuildOutput\WebApp\bin\SQLCE4Umbraco.xml" target="lib\net45\SQLCE4Umbraco.xml" />
<file src="..\_BuildOutput\WebApp\bin\System.Data.SqlServerCe.dll" target="lib\net45\System.Data.SqlServerCe.dll" />
<file src="..\_BuildOutput\WebApp\bin\System.Data.SqlServerCe.Entity.dll" target="lib\net45\System.Data.SqlServerCe.Entity.dll" />
<file src="..\_BuildOutput\WebApp\bin\TidyNet.dll" target="lib\net45\TidyNet.dll" />
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Core.dll" target="lib\net45\Umbraco.Core.dll" />
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Core.xml" target="lib\net45\Umbraco.Core.xml" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.DataLayer.dll" target="lib\net45\umbraco.DataLayer.dll" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.DataLayer.xml" target="lib\net45\umbraco.DataLayer.xml" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.dll" target="lib\net45\umbraco.dll" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.xml" target="lib\net45\umbraco.xml" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.editorControls.dll" target="lib\net45\umbraco.editorControls.dll" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.editorControls.xml" target="lib\net45\umbraco.editorControls.xml" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.MacroEngines.dll" target="lib\net45\umbraco.MacroEngines.dll" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.MacroEngines.xml" target="lib\net45\umbraco.MacroEngines.xml" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.providers.dll" target="lib\net45\umbraco.providers.dll" />
<file src="..\_BuildOutput\WebApp\bin\umbraco.providers.xml" target="lib\net45\umbraco.providers.xml" />
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Web.UI.dll" target="lib\net45\Umbraco.Web.UI.dll" />
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Web.UI.xml" target="lib\net45\Umbraco.Web.UI.xml" />
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.dll" target="lib\net45\UmbracoExamine.dll" />
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.xml" target="lib\net45\UmbracoExamine.xml" />
<file src="$BuildTmp$\WebApp\bin\businesslogic.dll" target="lib\net45\businesslogic.dll" />
<file src="$BuildTmp$\WebApp\bin\businesslogic.xml" target="lib\net45\businesslogic.xml" />
<file src="$BuildTmp$\WebApp\bin\cms.dll" target="lib\net45\cms.dll" />
<file src="$BuildTmp$\WebApp\bin\cms.xml" target="lib\net45\cms.xml" />
<file src="$BuildTmp$\WebApp\bin\controls.dll" target="lib\net45\controls.dll" />
<file src="$BuildTmp$\WebApp\bin\controls.xml" target="lib\net45\controls.xml" />
<file src="$BuildTmp$\WebApp\bin\interfaces.dll" target="lib\net45\interfaces.dll" />
<file src="$BuildTmp$\WebApp\bin\interfaces.xml" target="lib\net45\interfaces.xml" />
<file src="$BuildTmp$\WebApp\bin\log4net.dll" target="lib\net45\log4net.dll" />
<file src="$BuildTmp$\WebApp\bin\Microsoft.ApplicationBlocks.Data.dll" target="lib\net45\Microsoft.ApplicationBlocks.Data.dll" />
<file src="$BuildTmp$\WebApp\bin\SQLCE4Umbraco.dll" target="lib\net45\SQLCE4Umbraco.dll" />
<file src="$BuildTmp$\WebApp\bin\SQLCE4Umbraco.xml" target="lib\net45\SQLCE4Umbraco.xml" />
<file src="$BuildTmp$\WebApp\bin\System.Data.SqlServerCe.dll" target="lib\net45\System.Data.SqlServerCe.dll" />
<file src="$BuildTmp$\WebApp\bin\System.Data.SqlServerCe.Entity.dll" target="lib\net45\System.Data.SqlServerCe.Entity.dll" />
<file src="$BuildTmp$\WebApp\bin\TidyNet.dll" target="lib\net45\TidyNet.dll" />
<file src="$BuildTmp$\WebApp\bin\Umbraco.Core.dll" target="lib\net45\Umbraco.Core.dll" />
<file src="$BuildTmp$\WebApp\bin\Umbraco.Core.xml" target="lib\net45\Umbraco.Core.xml" />
<file src="$BuildTmp$\WebApp\bin\umbraco.DataLayer.dll" target="lib\net45\umbraco.DataLayer.dll" />
<file src="$BuildTmp$\WebApp\bin\umbraco.DataLayer.xml" target="lib\net45\umbraco.DataLayer.xml" />
<file src="$BuildTmp$\WebApp\bin\umbraco.dll" target="lib\net45\umbraco.dll" />
<file src="$BuildTmp$\WebApp\bin\umbraco.xml" target="lib\net45\umbraco.xml" />
<file src="$BuildTmp$\WebApp\bin\umbraco.editorControls.dll" target="lib\net45\umbraco.editorControls.dll" />
<file src="$BuildTmp$\WebApp\bin\umbraco.editorControls.xml" target="lib\net45\umbraco.editorControls.xml" />
<file src="$BuildTmp$\WebApp\bin\umbraco.MacroEngines.dll" target="lib\net45\umbraco.MacroEngines.dll" />
<file src="$BuildTmp$\WebApp\bin\umbraco.MacroEngines.xml" target="lib\net45\umbraco.MacroEngines.xml" />
<file src="$BuildTmp$\WebApp\bin\umbraco.providers.dll" target="lib\net45\umbraco.providers.dll" />
<file src="$BuildTmp$\WebApp\bin\umbraco.providers.xml" target="lib\net45\umbraco.providers.xml" />
<file src="$BuildTmp$\WebApp\bin\Umbraco.Web.UI.dll" target="lib\net45\Umbraco.Web.UI.dll" />
<file src="$BuildTmp$\WebApp\bin\Umbraco.Web.UI.xml" target="lib\net45\Umbraco.Web.UI.xml" />
<file src="$BuildTmp$\WebApp\bin\UmbracoExamine.dll" target="lib\net45\UmbracoExamine.dll" />
<file src="$BuildTmp$\WebApp\bin\UmbracoExamine.xml" target="lib\net45\UmbracoExamine.xml" />
<file src="tools\install.core.ps1" target="tools\install.ps1" />
<!-- Added to be able to produce a symbols package -->
<file src="..\_BuildOutput\bin\SQLCE4Umbraco.pdb" target="lib" />
<file src="$BuildTmp$\bin\SQLCE4Umbraco.pdb" target="lib" />
<file src="..\..\src\SQLCE4Umbraco\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\SQLCE4Umbraco" />
<file src="..\_BuildOutput\bin\businesslogic.pdb" target="lib" />
<file src="$BuildTmp$\bin\businesslogic.pdb" target="lib" />
<file src="..\..\src\umbraco.businesslogic\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.businesslogic" />
<file src="..\_BuildOutput\bin\cms.pdb" target="lib" />
<file src="$BuildTmp$\bin\cms.pdb" target="lib" />
<file src="..\..\src\umbraco.cms\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.cms" />
<file src="..\_BuildOutput\bin\controls.pdb" target="lib" />
<file src="$BuildTmp$\bin\controls.pdb" target="lib" />
<file src="..\..\src\umbraco.controls\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.controls" />
<file src="..\_BuildOutput\bin\interfaces.pdb" target="lib" />
<file src="$BuildTmp$\bin\interfaces.pdb" target="lib" />
<file src="..\..\src\umbraco.interfaces\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.interfaces" />
<file src="..\_BuildOutput\bin\Umbraco.Core.pdb" target="lib" />
<file src="$BuildTmp$\bin\Umbraco.Core.pdb" target="lib" />
<file src="..\..\src\Umbraco.Core\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Core" />
<file src="..\_BuildOutput\bin\umbraco.DataLayer.pdb" target="lib" />
<file src="$BuildTmp$\bin\umbraco.DataLayer.pdb" target="lib" />
<file src="..\..\src\umbraco.datalayer\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.datalayer" />
<file src="..\_BuildOutput\bin\umbraco.editorControls.pdb" target="lib" />
<file src="$BuildTmp$\bin\umbraco.editorControls.pdb" target="lib" />
<file src="..\..\src\umbraco.editorControls\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.editorControls" />
<file src="..\_BuildOutput\bin\umbraco.MacroEngines.pdb" target="lib" />
<file src="$BuildTmp$\bin\umbraco.MacroEngines.pdb" target="lib" />
<file src="..\..\src\umbraco.MacroEngines\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.MacroEngines" />
<file src="..\_BuildOutput\bin\umbraco.providers.pdb" target="lib" />
<file src="$BuildTmp$\bin\umbraco.providers.pdb" target="lib" />
<file src="..\..\src\umbraco.providers\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.providers" />
<file src="..\_BuildOutput\bin\umbraco.pdb" target="lib" />
<file src="$BuildTmp$\bin\umbraco.pdb" target="lib" />
<file src="..\..\src\Umbraco.Web\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Web" />
<file src="..\_BuildOutput\bin\Umbraco.Web.UI.pdb" target="lib" />
<file src="$BuildTmp$\bin\Umbraco.Web.UI.pdb" target="lib" />
<file src="..\..\src\Umbraco.Web.UI\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Web.UI" />
<file src="..\_BuildOutput\bin\UmbracoExamine.pdb" target="lib" />
<file src="$BuildTmp$\bin\UmbracoExamine.pdb" target="lib" />
<file src="..\..\src\UmbracoExamine\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\UmbracoExamine" />
</files>
</package>
+13 -13
View File
@@ -19,22 +19,22 @@
<dependency id="Newtonsoft.Json" version="[10.0.2, 11.0.0)" />
<dependency id="Umbraco.ModelsBuilder" version="[3.0.7, 4.0.0)" />
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.2.1, 3.0.0)" />
<dependency id="ImageProcessor.Web.Config" version="[2.3.0, 3.0.0)" />
<dependency id="ImageProcessor.Web.Config" version="[2.3.0, 3.0.0)" />
</dependencies>
</metadata>
<files>
<file src="..\_BuildOutput\Configs\**" target="Content\Config" exclude="..\_BuildOutput\Configs\Web.config.transform" />
<file src="..\_BuildOutput\WebApp\Views\**" target="Content\Views" exclude="..\_BuildOutput\WebApp\Views\Web.config" />
<file src="..\_BuildOutput\WebApp\default.aspx" target="Content\default.aspx" />
<file src="..\_BuildOutput\WebApp\Global.asax" target="Content\Global.asax" />
<file src="..\_BuildOutput\WebApp\Web.config" target="UmbracoFiles\Web.config" />
<file src="..\_BuildOutput\WebApp\App_Browsers\**" target="UmbracoFiles\App_Browsers" />
<file src="..\_BuildOutput\WebApp\bin\amd64\**" target="UmbracoFiles\bin\amd64" />
<file src="..\_BuildOutput\WebApp\bin\x86\**" target="UmbracoFiles\bin\x86" />
<file src="..\_BuildOutput\WebApp\config\splashes\**" target="UmbracoFiles\Config\splashes" />
<file src="..\_BuildOutput\WebApp\umbraco\**" target="UmbracoFiles\umbraco" />
<file src="..\_BuildOutput\WebApp\umbraco_client\**" target="UmbracoFiles\umbraco_client" />
<file src="..\_BuildOutput\WebApp\Media\Web.config" target="Content\Media\Web.config" />
<file src="$BuildTmp$\Configs\**" target="Content\Config" exclude="$BuildTmp$\Configs\Web.config.transform" />
<file src="$BuildTmp$\WebApp\Views\**" target="Content\Views" exclude="$BuildTmp$\WebApp\Views\Web.config" />
<file src="$BuildTmp$\WebApp\default.aspx" target="Content\default.aspx" />
<file src="$BuildTmp$\WebApp\Global.asax" target="Content\Global.asax" />
<file src="$BuildTmp$\WebApp\Web.config" target="UmbracoFiles\Web.config" />
<file src="$BuildTmp$\WebApp\App_Browsers\**" target="UmbracoFiles\App_Browsers" />
<file src="$BuildTmp$\WebApp\bin\amd64\**" target="UmbracoFiles\bin\amd64" />
<file src="$BuildTmp$\WebApp\bin\x86\**" target="UmbracoFiles\bin\x86" />
<file src="$BuildTmp$\WebApp\config\splashes\**" target="UmbracoFiles\Config\splashes" />
<file src="$BuildTmp$\WebApp\umbraco\**" target="UmbracoFiles\umbraco" />
<file src="$BuildTmp$\WebApp\umbraco_client\**" target="UmbracoFiles\umbraco_client" />
<file src="$BuildTmp$\WebApp\Media\Web.config" target="Content\Media\Web.config" />
<file src="tools\install.ps1" target="tools\install.ps1" />
<file src="tools\Readme.txt" target="tools\Readme.txt" />
<file src="tools\ReadmeUpgrade.txt" target="tools\ReadmeUpgrade.txt" />
Binary file not shown.
-2
View File
@@ -1,2 +0,0 @@
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
7.6.5
+67
View File
@@ -0,0 +1,67 @@
param (
[Parameter(Mandatory=$false)]
[string]
$version,
[Parameter(Mandatory=$false)]
[Alias("mo")]
[switch]
$moduleOnly = $false
)
# the script can run either from the solution root,
# or from the ./build directory - anything else fails
if ([System.IO.Path]::GetFileName($pwd) -eq "build")
{
$mpath = [System.IO.Path]::GetDirectoryName($pwd) + "\build\Modules\"
}
else
{
$mpath = "$pwd\build\Modules\"
}
# look for the module and throw if not found
if (-not [System.IO.Directory]::Exists($mpath + "Umbraco.Build"))
{
Write-Error "Could not locate Umbraco build Powershell module."
break
}
# add the module path (if not already there)
if (-not $env:PSModulePath.Contains($mpath))
{
$env:PSModulePath = "$mpath;$env:PSModulePath"
}
# force-import (or re-import) the module
Write-Host "Import Umbraco build Powershell module"
Import-Module Umbraco.Build -Force -DisableNameChecking
# module only?
if ($moduleOnly)
{
if (-not [string]::IsNullOrWhiteSpace($version))
{
Write-Host "(module only: ignoring version parameter)"
}
else
{
Write-Host "(module only)"
}
break
}
# get build environment
Write-Host "Setup Umbraco build Environment"
$uenv = Get-UmbracoBuildEnv
# set the version if any
if (-not [string]::IsNullOrWhiteSpace($version))
{
Write-Host "Set Umbraco version to $version"
Set-UmbracoVersion $version
}
# full umbraco build
Write-Host "Build Umbraco"
Build-Umbraco
+18
View File
@@ -0,0 +1,18 @@
# Usage: powershell .\setversion.ps1 7.6.8
# Or: powershell .\setversion 7.6.8-beta001
param (
[Parameter(Mandatory=$true)]
[string]
$version
)
# report
Write-Host "Setting Umbraco version to $version"
# import Umbraco Build PowerShell module - $pwd is ./build
$env:PSModulePath = "$pwd\Modules\;$env:PSModulePath"
Import-Module Umbraco.Build -Force -DisableNameChecking
# run commands
$version = Set-UmbracoVersion -Version $version
-6
View File
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<solution>
<add key="disableSourceControlIntegration" value="true" />
</solution>
</configuration>
Binary file not shown.
-138
View File
@@ -1,138 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">$(MSBuildProjectDirectory)\..\</SolutionDir>
<!-- Enable the restore command to run before builds -->
<RestorePackages Condition=" '$(RestorePackages)' == '' ">false</RestorePackages>
<!-- Property that enables building a package from a project -->
<BuildPackage Condition=" '$(BuildPackage)' == '' ">false</BuildPackage>
<!-- Determines if package restore consent is required to restore packages -->
<RequireRestoreConsent Condition=" '$(RequireRestoreConsent)' != 'false' ">true</RequireRestoreConsent>
<!-- Download NuGet.exe if it does not already exist -->
<DownloadNuGetExe Condition=" '$(DownloadNuGetExe)' == '' ">false</DownloadNuGetExe>
</PropertyGroup>
<ItemGroup Condition=" '$(PackageSources)' == '' ">
<!-- Package sources used to restore packages. By default, registered sources under %APPDATA%\NuGet\NuGet.Config will be used -->
<!-- The official NuGet package source (https://www.nuget.org/api/v2/) will be excluded if package sources are specified and it does not appear in the list -->
<!--
<PackageSource Include="https://www.nuget.org/api/v2/" />
<PackageSource Include="https://my-nuget-source/nuget/" />
-->
<PackageSource Include="https://nuget.org/api/v2/" />
<PackageSource Include="http://www.myget.org/F/umbracocore/" />
</ItemGroup>
<PropertyGroup Condition=" '$(OS)' == 'Windows_NT'">
<!-- Windows specific commands -->
<NuGetToolsPath>$([System.IO.Path]::Combine($(SolutionDir), ".nuget"))</NuGetToolsPath>
<PackagesConfig>$([System.IO.Path]::Combine($(ProjectDir), "packages.config"))</PackagesConfig>
</PropertyGroup>
<PropertyGroup Condition=" '$(OS)' != 'Windows_NT'">
<!-- We need to launch nuget.exe with the mono command if we're not on windows -->
<NuGetToolsPath>$(SolutionDir).nuget</NuGetToolsPath>
<PackagesConfig>packages.config</PackagesConfig>
</PropertyGroup>
<PropertyGroup>
<!-- NuGet command -->
<NuGetExePath Condition=" '$(NuGetExePath)' == '' ">$(NuGetToolsPath)\NuGet.exe</NuGetExePath>
<PackageSources Condition=" $(PackageSources) == '' ">@(PackageSource)</PackageSources>
<NuGetCommand Condition=" '$(OS)' == 'Windows_NT'">"$(NuGetExePath)"</NuGetCommand>
<NuGetCommand Condition=" '$(OS)' != 'Windows_NT' ">mono --runtime=v4.0.30319 $(NuGetExePath)</NuGetCommand>
<PackageOutputDir Condition="$(PackageOutputDir) == ''">$(TargetDir.Trim('\\'))</PackageOutputDir>
<RequireConsentSwitch Condition=" $(RequireRestoreConsent) == 'true' ">-RequireConsent</RequireConsentSwitch>
<NonInteractiveSwitch Condition=" '$(VisualStudioVersion)' != '' AND '$(OS)' == 'Windows_NT' ">-NonInteractive</NonInteractiveSwitch>
<PaddedSolutionDir Condition=" '$(OS)' == 'Windows_NT'">"$(SolutionDir) "</PaddedSolutionDir>
<PaddedSolutionDir Condition=" '$(OS)' != 'Windows_NT' ">"$(SolutionDir)"</PaddedSolutionDir>
<!-- Commands -->
<RestoreCommand>$(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)</RestoreCommand>
<BuildCommand>$(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols</BuildCommand>
<!-- We need to ensure packages are restored prior to assembly resolve -->
<BuildDependsOn Condition="$(RestorePackages) == 'true'">
RestorePackages;
$(BuildDependsOn);
</BuildDependsOn>
<!-- Make the build depend on restore packages -->
<BuildDependsOn Condition="$(BuildPackage) == 'true'">
$(BuildDependsOn);
BuildPackage;
</BuildDependsOn>
</PropertyGroup>
<Target Name="CheckPrerequisites">
<!-- Raise an error if we're unable to locate nuget.exe -->
<Error Condition="'$(DownloadNuGetExe)' != 'true' AND !Exists('$(NuGetExePath)')" Text="Unable to locate '$(NuGetExePath)'" />
<!--
Take advantage of MsBuild's build dependency tracking to make sure that we only ever download nuget.exe once.
This effectively acts as a lock that makes sure that the download operation will only happen once and all
parallel builds will have to wait for it to complete.
-->
<MsBuild Targets="_DownloadNuGet" Projects="$(MSBuildThisFileFullPath)" Properties="Configuration=NOT_IMPORTANT;DownloadNuGetExe=$(DownloadNuGetExe)" />
</Target>
<Target Name="_DownloadNuGet">
<DownloadNuGet OutputFilename="$(NuGetExePath)" Condition=" '$(DownloadNuGetExe)' == 'true' AND !Exists('$(NuGetExePath)')" />
</Target>
<Target Name="RestorePackages" DependsOnTargets="CheckPrerequisites">
<Exec Command="$(RestoreCommand)"
Condition="'$(OS)' != 'Windows_NT' And Exists('$(PackagesConfig)')" />
<Exec Command="$(RestoreCommand)"
LogStandardErrorAsError="true"
Condition="'$(OS)' == 'Windows_NT' And Exists('$(PackagesConfig)')" />
</Target>
<Target Name="BuildPackage" DependsOnTargets="CheckPrerequisites">
<Exec Command="$(BuildCommand)"
Condition=" '$(OS)' != 'Windows_NT' " />
<Exec Command="$(BuildCommand)"
LogStandardErrorAsError="true"
Condition=" '$(OS)' == 'Windows_NT' " />
</Target>
<UsingTask TaskName="DownloadNuGet" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<OutputFilename ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Reference Include="System.Core" />
<Using Namespace="System" />
<Using Namespace="System.IO" />
<Using Namespace="System.Net" />
<Using Namespace="Microsoft.Build.Framework" />
<Using Namespace="Microsoft.Build.Utilities" />
<Code Type="Fragment" Language="cs">
<![CDATA[
try {
OutputFilename = Path.GetFullPath(OutputFilename);
Log.LogMessage("Downloading latest version of NuGet.exe...");
WebClient webClient = new WebClient();
webClient.DownloadFile("https://www.nuget.org/nuget.exe", OutputFilename);
return true;
}
catch (Exception ex) {
Log.LogErrorFromException(ex);
return false;
}
]]>
</Code>
</Task>
</UsingTask>
</Project>
-1
View File
@@ -93,7 +93,6 @@
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
+2 -2
View File
@@ -11,5 +11,5 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.6.5")]
[assembly: AssemblyInformationalVersion("7.6.5")]
[assembly: AssemblyFileVersion("7.6.8")]
[assembly: AssemblyInformationalVersion("7.6.8")]
@@ -0,0 +1,104 @@
using System;
using System.Threading;
using System.Web;
using Umbraco.Core.Security;
namespace Umbraco.Core.Auditing
{
/// <summary>
/// This class is used by events raised from hthe BackofficeUserManager
/// </summary>
public class IdentityAuditEventArgs : EventArgs
{
/// <summary>
/// The action that got triggered from the audit event
/// </summary>
public AuditEvent Action { get; private set; }
/// <summary>
/// Current date/time in UTC format
/// </summary>
public DateTime DateTimeUtc { get; private set; }
/// <summary>
/// The source IP address of the user performing the action
/// </summary>
public string IpAddress { get; private set; }
/// <summary>
/// The user affected by the event raised
/// </summary>
public int AffectedUser { get; private set; }
/// <summary>
/// If a user is perfoming an action on a different user, then this will be set. Otherwise it will be -1
/// </summary>
public int PerformingUser { get; private set; }
/// <summary>
/// An optional comment about the action being logged
/// </summary>
public string Comment { get; private set; }
/// <summary>
/// This property is always empty except in the LoginFailed event for an unknown user trying to login
/// </summary>
public string Username { get; private set; }
/// <summary>
/// Sets the properties on the event being raised, all parameters are optional except for the action being performed
/// </summary>
/// <param name="action">An action based on the AuditEvent enum</param>
/// <param name="ipAddress">The client's IP address. This is usually automatically set but could be overridden if necessary</param>
/// <param name="performingUser">The Id of the user performing the action (if different from the user affected by the action)</param>
public IdentityAuditEventArgs(AuditEvent action, string ipAddress, int performingUser = -1)
{
DateTimeUtc = DateTime.UtcNow;
Action = action;
IpAddress = ipAddress;
PerformingUser = performingUser == -1
? GetCurrentRequestBackofficeUserId()
: performingUser;
}
public IdentityAuditEventArgs(AuditEvent action, string ipAddress, string username, string comment)
{
DateTimeUtc = DateTime.UtcNow;
Action = action;
IpAddress = ipAddress;
Username = username;
Comment = comment;
}
/// <summary>
/// Returns the current logged in backoffice user's Id logging if there is one
/// </summary>
/// <returns></returns>
protected int GetCurrentRequestBackofficeUserId()
{
var userId = -1;
var backOfficeIdentity = Thread.CurrentPrincipal.GetUmbracoIdentity();
if (backOfficeIdentity != null)
int.TryParse(backOfficeIdentity.Id.ToString(), out userId);
return userId;
}
}
public enum AuditEvent
{
AccountLocked,
AccountUnlocked,
ForgotPasswordRequested,
ForgotPasswordChangedSuccess,
LoginFailed,
LoginRequiresVerification,
LoginSucces,
LogoutSuccess,
PasswordChanged,
PasswordReset,
ResetAccessFailedCount
}
}
@@ -1,12 +1,19 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using System.Web;
using System.Xml.Linq;
using ClientDependency.Core.CompositeFiles.Providers;
using ClientDependency.Core.Config;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
namespace Umbraco.Core.Configuration
{
internal class ClientDependencyConfiguration
{
/// <summary>
/// A utility class for working with CDF config and cache files - use sparingly!
/// </summary>
public class ClientDependencyConfiguration
{
private readonly ILogger _logger;
private readonly string _fileName;
@@ -21,7 +28,7 @@ namespace Umbraco.Core.Configuration
/// <summary>
/// Changes the version number in ClientDependency.config to a random value to avoid stale caches
/// </summary>
internal bool IncreaseVersionNumber()
public bool IncreaseVersionNumber()
{
try
{
@@ -49,5 +56,55 @@ namespace Umbraco.Core.Configuration
return false;
}
/// <summary>
/// Clears the temporary files stored for the ClientDependency folder
/// </summary>
/// <param name="currentHttpContext"></param>
public bool ClearTempFiles(HttpContextBase currentHttpContext)
{
var cdfTempDirectories = new HashSet<string>();
foreach (BaseCompositeFileProcessingProvider provider in ClientDependencySettings.Instance
.CompositeFileProcessingProviderCollection)
{
var path = provider.CompositeFilePath.FullName;
cdfTempDirectories.Add(path);
}
try
{
var fullPath = currentHttpContext.Server.MapPath(XmlFileMapper.FileMapVirtualFolder);
if (fullPath != null)
{
cdfTempDirectories.Add(fullPath);
}
}
catch (Exception ex)
{
//invalid path format or something... try/catch to be safe
LogHelper.Error<ClientDependencyConfiguration>("Could not get path from ClientDependency.config", ex);
}
var success = true;
foreach (var directory in cdfTempDirectories)
{
var directoryInfo = new DirectoryInfo(directory);
if (directoryInfo.Exists == false)
continue;
try
{
directoryInfo.Delete(true);
}
catch (Exception ex)
{
// Something could be locking the directory or the was another error, making sure we don't break the upgrade installer
LogHelper.Error<ClientDependencyConfiguration>("Could not clear temp files", ex);
success = false;
}
}
return success;
}
}
}
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration
{
public class UmbracoVersion
{
private static readonly Version Version = new Version("7.6.5");
private static readonly Version Version = new Version("7.6.8");
/// <summary>
/// Gets the current version of Umbraco.
+10
View File
@@ -222,6 +222,16 @@ namespace Umbraco.Core
/// Guid for a Forms DataSource.
/// </summary>
public static readonly Guid LanguageGuid = new Guid(Language);
/// <summary>
/// Guid for an Identifier Reservation.
/// </summary>
public const string IdReservation = "92849B1E-3904-4713-9356-F646F87C25F4";
/// <summary>
/// Guid for an Identifier Reservation.
/// </summary>
public static readonly Guid IdReservationGuid = new Guid(IdReservation);
}
}
}
+2
View File
@@ -14,6 +14,8 @@ namespace Umbraco.Core
public const string BackOfficeTokenAuthenticationType = "UmbracoBackOfficeToken";
public const string BackOfficeTwoFactorAuthenticationType = "UmbracoTwoFactorCookie";
internal const string EmptyPasswordPrefix = "___UIDEMPTYPWORD__";
/// <summary>
/// The prefix used for external identity providers for their authentication type
/// </summary>
+23
View File
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web;
using AutoMapper;
@@ -399,6 +400,28 @@ namespace Umbraco.Core
if (ApplicationContext.IsConfigured == false) return;
if (ApplicationContext.DatabaseContext.IsDatabaseConfigured == false) return;
// deal with localdb
var databaseContext = ApplicationContext.DatabaseContext;
var localdbex = new Regex(@"\(localdb\)\\([a-zA-Z0-9-_]+)(;|$)");
var m = localdbex.Match(databaseContext.ConnectionString);
if (m.Success)
{
var instanceName = m.Groups[1].Value;
ProfilingLogger.Logger.Info<CoreBootManager>(string.Format("LocalDb instance \"{0}\"", instanceName));
var localDb = new LocalDb();
if (localDb.IsAvailable == false)
throw new UmbracoStartupFailedException("Umbraco cannot start. LocalDb is not available.");
if (localDb.InstanceExists(m.Groups[1].Value) == false)
{
if (localDb.CreateInstance(instanceName) == false)
throw new UmbracoStartupFailedException(string.Format("Umbraco cannot start. LocalDb cannot create instance \"{0}\".", instanceName));
if (localDb.StartInstance(instanceName) == false)
throw new UmbracoStartupFailedException(string.Format("Umbraco cannot start. LocalDb cannot start instance \"{0}\".", instanceName));
}
}
//try now
if (ApplicationContext.DatabaseContext.CanConnect)
return;
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Threading;
namespace Umbraco.Core.Deploy
{
@@ -38,5 +39,10 @@ namespace Umbraco.Core.Deploy
/// <param name="key">The key of the item.</param>
/// <returns>The item with the specified key and type, if any, else null.</returns>
T Item<T>(string key) where T : class;
///// <summary>
///// Gets the global deployment cancellation token.
///// </summary>
//CancellationToken CancellationToken { get; }
}
}
+11
View File
@@ -295,5 +295,16 @@ namespace Umbraco.Core
return list1Groups.Count == list2Groups.Count
&& list1Groups.All(g => g.Count() == list2Groups[g.Key].Count());
}
public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source)
{
using (var e = source.GetEnumerator())
{
if (e.MoveNext() == false) yield break;
for (var value = e.Current; e.MoveNext(); value = e.Current)
yield return value;
}
}
}
}
+20 -11
View File
@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Security.Permissions;
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Core.Events
{
@@ -13,6 +12,9 @@ namespace Umbraco.Core.Events
public class CancellableEventArgs : EventArgs, IEquatable<CancellableEventArgs>
{
private bool _cancel;
private Dictionary<string, object> _eventState;
private static readonly ReadOnlyDictionary<string, object> EmptyAdditionalData = new ReadOnlyDictionary<string, object>(new Dictionary<string, object>());
public CancellableEventArgs(bool canCancel, EventMessages messages, IDictionary<string, object> additionalData)
{
@@ -26,7 +28,7 @@ namespace Umbraco.Core.Events
if (eventMessages == null) throw new ArgumentNullException("eventMessages");
CanCancel = canCancel;
Messages = eventMessages;
AdditionalData = new ReadOnlyDictionary<string, object>(new Dictionary<string, object>());
AdditionalData = EmptyAdditionalData;
}
public CancellableEventArgs(bool canCancel)
@@ -34,18 +36,16 @@ namespace Umbraco.Core.Events
CanCancel = canCancel;
//create a standalone messages
Messages = new EventMessages();
AdditionalData = new ReadOnlyDictionary<string, object>(new Dictionary<string, object>());
AdditionalData = EmptyAdditionalData;
}
public CancellableEventArgs(EventMessages eventMessages)
: this(true, eventMessages)
{
}
{ }
public CancellableEventArgs()
: this(true)
{
}
{ }
/// <summary>
/// Flag to determine if this instance will support being cancellable
@@ -95,10 +95,19 @@ namespace Umbraco.Core.Events
/// In some cases raised evens might need to contain additional arbitrary readonly data which can be read by event subscribers
/// </summary>
/// <remarks>
/// This allows for a bit of flexibility in our event raising - it's not pretty but we need to maintain backwards compatibility
/// This allows for a bit of flexibility in our event raising - it's not pretty but we need to maintain backwards compatibility
/// so we cannot change the strongly typed nature for some events.
/// </remarks>
public ReadOnlyDictionary<string, object> AdditionalData { get; private set; }
/// <summary>
/// This can be used by event subscribers to store state in the event args so they easily deal with custom state data between a starting ("ing")
/// event and an ending ("ed") event
/// </summary>
public IDictionary<string, object> EventState
{
get { return _eventState ?? (_eventState = new Dictionary<string, object>()); }
}
public bool Equals(CancellableEventArgs other)
{
@@ -111,13 +120,13 @@ namespace Umbraco.Core.Events
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
if (obj.GetType() != GetType()) return false;
return Equals((CancellableEventArgs) obj);
}
public override int GetHashCode()
{
return (AdditionalData != null ? AdditionalData.GetHashCode() : 0);
return AdditionalData != null ? AdditionalData.GetHashCode() : 0;
}
public static bool operator ==(CancellableEventArgs left, CancellableEventArgs right)
@@ -127,7 +136,7 @@ namespace Umbraco.Core.Events
public static bool operator !=(CancellableEventArgs left, CancellableEventArgs right)
{
return !Equals(left, right);
return Equals(left, right) == false;
}
}
}
+2 -1
View File
@@ -99,7 +99,8 @@ namespace Umbraco.Core.Events
/// </summary>
public IEnumerable<TEntity> DeletedEntities
{
get { return EventObject; }
get { return EventObject; }
internal set { EventObject = value; }
}
/// <summary>
+20 -1
View File
@@ -6,6 +6,8 @@ namespace Umbraco.Core.Events
{
public class MoveEventArgs<TEntity> : CancellableObjectEventArgs<TEntity>, IEquatable<MoveEventArgs<TEntity>>
{
private IEnumerable<MoveEventInfo<TEntity>> _moveInfoCollection;
/// <summary>
/// Constructor accepting a collection of MoveEventInfo objects
/// </summary>
@@ -107,7 +109,24 @@ namespace Umbraco.Core.Events
/// <summary>
/// Gets all MoveEventInfo objects used to create the object
/// </summary>
public IEnumerable<MoveEventInfo<TEntity>> MoveInfoCollection { get; private set; }
public IEnumerable<MoveEventInfo<TEntity>> MoveInfoCollection
{
get { return _moveInfoCollection; }
set
{
var first = value.FirstOrDefault();
if (first == null)
{
throw new InvalidOperationException("MoveInfoCollection must have at least one item");
}
_moveInfoCollection = value;
//assign the legacy props
EventObject = first.Entity;
ParentId = first.NewParentId;
}
}
/// <summary>
/// The entity being moved
@@ -107,7 +107,7 @@ namespace Umbraco.Core.Events
/// <summary>
/// Boolean indicating whether the Recycle Bin was emptied successfully
/// </summary>
public bool RecycleBinEmptiedSuccessfully { get; private set; }
public bool RecycleBinEmptiedSuccessfully { get; set; }
/// <summary>
/// Boolean indicating whether this event was fired for the Content's Recycle Bin.
+10 -6
View File
@@ -32,7 +32,11 @@ namespace Umbraco.Core
public GuidUdi(Uri uriValue)
: base(uriValue)
{
Guid = Guid.Parse(uriValue.AbsolutePath.TrimStart('/'));
Guid guid;
if (Guid.TryParse(uriValue.AbsolutePath.TrimStart('/'), out guid) == false)
throw new FormatException("Url \"" + uriValue + "\" is not a guid entity id.");
Guid = guid;
}
/// <summary>
@@ -43,16 +47,17 @@ namespace Umbraco.Core
public new static GuidUdi Parse(string s)
{
var udi = Udi.Parse(s);
if (!(udi is GuidUdi))
if (udi is GuidUdi == false)
throw new FormatException("String \"" + s + "\" is not a guid entity id.");
return (GuidUdi)udi;
return (GuidUdi) udi;
}
public static bool TryParse(string s, out GuidUdi udi)
{
Udi tmp;
udi = null;
if (!TryParse(s, out tmp)) return false;
if (TryParse(s, out tmp) == false) return false;
udi = tmp as GuidUdi;
return udi != null;
}
@@ -75,10 +80,9 @@ namespace Umbraco.Core
get { return Guid == Guid.Empty; }
}
/// <inheritdoc/>
public GuidUdi EnsureClosed()
{
base.EnsureNotRoot();
EnsureNotRoot();
return this;
}
}
+4 -2
View File
@@ -2,17 +2,19 @@
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
namespace Umbraco.Core
{
/// <summary>
/// Used to create a hash code from multiple objects.
/// Used to create a .NET HashCode from multiple objects.
/// </summary>
/// <remarks>
/// .Net has a class the same as this: System.Web.Util.HashCodeCombiner and of course it works for all sorts of things
/// which we've not included here as we just need a quick easy class for this in order to create a unique
/// hash of directories/files to see if they have changed.
///
/// NOTE: It's probably best to not relying on the hashing result across AppDomains! If you need a constant/reliable hash value
/// between AppDomains use SHA1. This is perfect for hashing things in a very fast way for a single AppDomain.
/// </remarks>
internal class HashCodeCombiner
{
+154
View File
@@ -0,0 +1,154 @@
using System;
using System.Globalization;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace Umbraco.Core
{
/// <summary>
/// Used to generate a string hash using crypto libraries over multiple objects
/// </summary>
/// <remarks>
/// This should be used to generate a reliable hash that survives AppDomain restarts.
/// This will use the crypto libs to generate the hash and will try to ensure that
/// strings, etc... are not re-allocated so it's not consuming much memory.
/// </remarks>
internal class HashGenerator : DisposableObject
{
public HashGenerator()
{
_writer = new StreamWriter(_ms, Encoding.Unicode, 1024, leaveOpen: true);
}
private readonly MemoryStream _ms = new MemoryStream();
private StreamWriter _writer;
internal void AddInt(int i)
{
_writer.Write(i);
}
internal void AddLong(long i)
{
_writer.Write(i);
}
internal void AddObject(object o)
{
_writer.Write(o);
}
internal void AddDateTime(DateTime d)
{
_writer.Write(d.Ticks);;
}
internal void AddString(string s)
{
if (s != null)
_writer.Write(s);
}
internal void AddCaseInsensitiveString(string s)
{
//I've tried to no allocate a new string with this which can be done if we use the CompareInfo.GetSortKey method which will create a new
//byte array that we can use to write to the output, however this also allocates new objects so i really don't think the performance
//would be much different. In any case, i'll leave this here for reference. We could write the bytes out based on the sort key,
//this is how we could deal with case insensitivity without allocating another string
//for reference see: https://stackoverflow.com/a/10452967/694494
//we could go a step further and s.Normalize() but we're not really dealing with crazy unicode with this class so far.
if (s != null)
_writer.Write(s.ToUpperInvariant());
}
internal void AddFileSystemItem(FileSystemInfo f)
{
//if it doesn't exist, don't proceed.
if (f.Exists == false)
return;
AddCaseInsensitiveString(f.FullName);
AddDateTime(f.CreationTimeUtc);
AddDateTime(f.LastWriteTimeUtc);
//check if it is a file or folder
var fileInfo = f as FileInfo;
if (fileInfo != null)
{
AddLong(fileInfo.Length);
}
var dirInfo = f as DirectoryInfo;
if (dirInfo != null)
{
foreach (var d in dirInfo.GetFiles())
{
AddFile(d);
}
foreach (var s in dirInfo.GetDirectories())
{
AddFolder(s);
}
}
}
internal void AddFile(FileInfo f)
{
AddFileSystemItem(f);
}
internal void AddFolder(DirectoryInfo d)
{
AddFileSystemItem(d);
}
/// <summary>
/// Returns the generated hash output of all added objects
/// </summary>
/// <returns></returns>
internal string GenerateHash()
{
//flush,close,dispose the writer,then create a new one since it's possible to keep adding after GenerateHash is called.
_writer.Flush();
_writer.Close();
_writer.Dispose();
_writer = new StreamWriter(_ms, Encoding.UTF8, 1024, leaveOpen: true);
var hashType = CryptoConfig.AllowOnlyFipsAlgorithms ? "SHA1" : "MD5";
//create an instance of the correct hashing provider based on the type passed in
var hasher = HashAlgorithm.Create(hashType);
if (hasher == null) throw new InvalidOperationException("No hashing type found by name " + hashType);
using (hasher)
{
var buffer = _ms.GetBuffer();
//get the hashed values created by our selected provider
var hashedByteArray = hasher.ComputeHash(buffer);
//create a StringBuilder object
var stringBuilder = new StringBuilder();
//loop to each each byte
foreach (var b in hashedByteArray)
{
//append it to our StringBuilder
stringBuilder.Append(b.ToString("x2"));
}
//return the hashed value
return stringBuilder.ToString();
}
}
protected override void DisposeResources()
{
_writer.Close();
_writer.Dispose();
_ms.Close();
_ms.Dispose();
}
}
}
-3
View File
@@ -1,8 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
+6 -2
View File
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
@@ -264,7 +263,7 @@ namespace Umbraco.Core.Models
/// <returns><see cref="Property"/> Value as an <see cref="object"/></returns>
public virtual object GetValue(string propertyTypeAlias)
{
return Properties[propertyTypeAlias].Value;
return Properties.Contains(propertyTypeAlias) ? Properties[propertyTypeAlias].Value : null;
}
/// <summary>
@@ -275,6 +274,11 @@ namespace Umbraco.Core.Models
/// <returns><see cref="Property"/> Value as a <see cref="TPassType"/></returns>
public virtual TPassType GetValue<TPassType>(string propertyTypeAlias)
{
if (Properties.Contains(propertyTypeAlias) == false)
{
return default(TPassType);
}
var convertAttempt = Properties[propertyTypeAlias].Value.TryConvertTo<TPassType>();
return convertAttempt.Success ? convertAttempt.Result : default(TPassType);
}
+1 -4
View File
@@ -1,10 +1,7 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
namespace Umbraco.Core.Models.EntityBase
{
@@ -102,7 +99,7 @@ namespace Umbraco.Core.Models.EntityBase
}
[IgnoreDataMember]
public DateTime? DeletedDate { get; set; }
public DateTime? DeletedDate { get; set; }
internal virtual void ResetIdentity()
{
@@ -42,7 +42,7 @@ namespace Umbraco.Core.Models.Identity
private string GetPasswordHash(string storedPass)
{
return storedPass.StartsWith("___UIDEMPTYPWORD__") ? null : storedPass;
return storedPass.StartsWith(Constants.Security.EmptyPasswordPrefix) ? null : storedPass;
}
}
}
@@ -93,6 +93,10 @@ namespace Umbraco.Core.Models
//NOTE: Consider checking type before value is set: item.PropertyType.DataTypeId == property.PropertyType.DataTypeId
//Transfer the existing value to the new property
var property = this[key];
if (item.Id == 0 && property.Id != 0)
{
item.Id = property.Id;
}
if (item.Value == null && property.Value != null)
{
item.Value = property.Value;
@@ -185,6 +185,13 @@ namespace Umbraco.Core.Models
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.Language)]
[FriendlyName("Language")]
Language
Language,
/// <summary>
/// Reserved Identifier
/// </summary>
[UmbracoObjectType(Constants.ObjectTypes.IdReservation)]
[FriendlyName("Identifier Reservation")]
IdReservation
}
}
+3 -2
View File
@@ -1,5 +1,6 @@
using System;
using System.Globalization;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Services;
@@ -12,7 +13,7 @@ namespace Umbraco.Core.Models
/// </summary>
/// <param name="user"></param>
/// <param name="textService"></param>
/// <returns></returns>
/// <returns></returns>
public static CultureInfo GetUserCulture(this IUser user, ILocalizedTextService textService)
{
if (user == null) throw new ArgumentNullException("user");
@@ -34,7 +35,7 @@ namespace Umbraco.Core.Models
catch (CultureNotFoundException)
{
//return the default one
return CultureInfo.GetCultureInfo("en");
return CultureInfo.GetCultureInfo(GlobalSettings.DefaultUILanguage);
}
}
+959
View File
@@ -0,0 +1,959 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.IO;
using System.Linq;
namespace Umbraco.Core.Persistence
{
/// <summary>
/// Manages LocalDB databases.
/// </summary>
/// <remarks>
/// <para>Latest version is SQL Server 2016 Express LocalDB,
/// see https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/sql-server-2016-express-localdb
/// which can be installed by downloading the Express installer from https://www.microsoft.com/en-us/sql-server/sql-server-downloads
/// (about 5MB) then select 'download media' to download SqlLocalDB.msi (about 44MB), which you can execute. This installs
/// LocalDB only. Though you probably want to install the full Express. You may also want to install SQL Server Management
/// Studio which can be used to connect to LocalDB databases.</para>
/// <para>See also https://github.com/ritterim/automation-sql which is a somewhat simpler version of this.</para>
/// </remarks>
internal class LocalDb
{
private int _version;
private bool _hasVersion;
#region Availability & Version
/// <summary>
/// Gets the LocalDb installed version.
/// </summary>
/// <remarks>If more than one version is installed, returns the highest available. Returns
/// the major version as an integer e.g. 11, 12...</remarks>
/// <exception cref="InvalidOperationException">Thrown when LocalDb is not available.</exception>
public int Version
{
get
{
EnsureVersion();
if (_version <= 0)
throw new InvalidOperationException("LocalDb is not available.");
return _version;
}
}
/// <summary>
/// Ensures that the LocalDb version is detected.
/// </summary>
private void EnsureVersion()
{
if (_hasVersion) return;
DetectVersion();
_hasVersion = true;
}
/// <summary>
/// Gets a value indicating whether LocalDb is available.
/// </summary>
public bool IsAvailable
{
get
{
EnsureVersion();
return _version > 0;
}
}
/// <summary>
/// Ensures that LocalDb is available.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown when LocalDb is not available.</exception>
private void EnsureAvailable()
{
if (IsAvailable == false)
throw new InvalidOperationException("LocalDb is not available.");
}
/// <summary>
/// Detects LocalDb installed version.
/// </summary>
/// <remarks>If more than one version is installed, the highest available is detected.</remarks>
private void DetectVersion()
{
_hasVersion = true;
_version = -1;
var programFiles = Environment.GetEnvironmentVariable("ProgramFiles");
if (programFiles == null) return;
// detect 14, 13, 12, 11
for (var i = 14; i > 10; i--)
{
var path = Path.Combine(programFiles, string.Format(@"Microsoft SQL Server\{0}0\Tools\Binn\SqlLocalDB.exe", i));
if (File.Exists(path) == false) continue;
_version = i;
break;
}
}
#endregion
#region Instances
/// <summary>
/// Gets the name of existing LocalDb instances.
/// </summary>
/// <returns>The name of existing LocalDb instances.</returns>
/// <exception cref="InvalidOperationException">Thrown when LocalDb is not available.</exception>
public string[] GetInstances()
{
EnsureAvailable();
string output, error;
var rc = ExecuteSqlLocalDb("i", out output, out error); // info
if (rc != 0 || error != string.Empty) return null;
return output.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
}
/// <summary>
/// Gets a value indicating whether a LocalDb instance exists.
/// </summary>
/// <param name="instanceName">The name of the instance.</param>
/// <returns>A value indicating whether a LocalDb instance with the specified name exists.</returns>
/// <exception cref="InvalidOperationException">Thrown when LocalDb is not available.</exception>
public bool InstanceExists(string instanceName)
{
EnsureAvailable();
var instances = GetInstances();
return instances != null && instances.Contains(instanceName);
}
/// <summary>
/// Creates a LocalDb instance.
/// </summary>
/// <param name="instanceName">The name of the instance.</param>
/// <returns>A value indicating whether the instance was created without errors.</returns>
/// <exception cref="InvalidOperationException">Thrown when LocalDb is not available.</exception>
public bool CreateInstance(string instanceName)
{
EnsureAvailable();
string output, error;
return ExecuteSqlLocalDb(string.Format("c \"{0}\"", instanceName), out output, out error) == 0 && error == string.Empty;
}
/// <summary>
/// Drops a LocalDb instance.
/// </summary>
/// <param name="instanceName">The name of the instance.</param>
/// <returns>A value indicating whether the instance was dropped without errors.</returns>
/// <exception cref="InvalidOperationException">Thrown when LocalDb is not available.</exception>
/// <remarks>
/// When an instance is dropped all the attached database files are deleted.
/// Successful if the instance does not exist.
/// </remarks>
public bool DropInstance(string instanceName)
{
EnsureAvailable();
var instance = GetInstance(instanceName);
if (instance == null) return true;
instance.DropDatabases(); // else the files remain
// -i force NOWAIT, -k kills
string output, error;
return ExecuteSqlLocalDb(string.Format("p \"{0}\" -i", instanceName), out output, out error) == 0 && error == string.Empty
&& ExecuteSqlLocalDb(string.Format("d \"{0}\"", instanceName), out output, out error) == 0 && error == string.Empty;
}
/// <summary>
/// Stops a LocalDb instance.
/// </summary>
/// <param name="instanceName">The name of the instance.</param>
/// <returns>A value indicating whether the instance was stopped without errors.</returns>
/// <exception cref="InvalidOperationException">Thrown when LocalDb is not available.</exception>
/// <remarks>
/// Successful if the instance does not exist.
/// </remarks>
public bool StopInstance(string instanceName)
{
EnsureAvailable();
if (InstanceExists(instanceName) == false) return true;
// -i force NOWAIT, -k kills
string output, error;
return ExecuteSqlLocalDb(string.Format("p \"{0}\" -i", instanceName), out output, out error) == 0 && error == string.Empty;
}
/// <summary>
/// Stops a LocalDb instance.
/// </summary>
/// <param name="instanceName">The name of the instance.</param>
/// <returns>A value indicating whether the instance was started without errors.</returns>
/// <exception cref="InvalidOperationException">Thrown when LocalDb is not available.</exception>
/// <remarks>
/// Failed if the instance does not exist.
/// </remarks>
public bool StartInstance(string instanceName)
{
EnsureAvailable();
if (InstanceExists(instanceName) == false) return false;
string output, error;
return ExecuteSqlLocalDb(string.Format("s \"{0}\"", instanceName), out output, out error) == 0 && error == string.Empty;
}
/// <summary>
/// Gets a LocalDb instance.
/// </summary>
/// <param name="instanceName">The name of the instance.</param>
/// <returns>The instance with the specified name if it exists, otherwise null.</returns>
/// <exception cref="InvalidOperationException">Thrown when LocalDb is not available.</exception>
public Instance GetInstance(string instanceName)
{
EnsureAvailable();
return InstanceExists(instanceName) ? new Instance(instanceName) : null;
}
#endregion
#region Databases
/// <summary>
/// Represents a LocalDb instance.
/// </summary>
/// <remarks>
/// LocalDb is assumed to be available, and the instance is assumed to exist.
/// </remarks>
public class Instance
{
private readonly string _masterCstr;
/// <summary>
/// Gets the name of the instance.
/// </summary>
public string InstanceName { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="Instance"/> class.
/// </summary>
/// <param name="instanceName"></param>
public Instance(string instanceName)
{
InstanceName = instanceName;
_masterCstr = string.Format(@"Server=(localdb)\{0};Integrated Security=True;", instanceName);
}
/// <summary>
/// Gets a LocalDb connection string.
/// </summary>
/// <param name="databaseName">The name of the database.</param>
/// <returns>The connection string for the specified database.</returns>
/// <remarks>
/// The database should exist in the LocalDb instance.
/// </remarks>
public string GetConnectionString(string databaseName)
{
return _masterCstr + string.Format(@"Database={0};", databaseName);
}
/// <summary>
/// Gets a LocalDb connection string for an attached database.
/// </summary>
/// <param name="databaseName">The name of the database.</param>
/// <param name="filesPath">The directory containing database files.</param>
/// <returns>The connection string for the specified database.</returns>
/// <remarks>
/// The database should not exist in the LocalDb instance.
/// It will be attached with its name being its MDF filename (full path), uppercased, when
/// the first connection is opened, and remain attached until explicitely detached.
/// </remarks>
public string GetAttachedConnectionString(string databaseName, string filesPath)
{
string logName, baseFilename, baseLogFilename, mdfFilename, ldfFilename;
GetDatabaseFiles(databaseName, filesPath, out logName, out baseFilename, out baseLogFilename, out mdfFilename, out ldfFilename);
return _masterCstr + string.Format(@"AttachDbFileName='{0}';", mdfFilename);
}
/// <summary>
/// Gets the name of existing databases.
/// </summary>
/// <returns>The name of existing databases.</returns>
public string[] GetDatabases()
{
var userDatabases = new List<string>();
using (var conn = new SqlConnection(_masterCstr))
using (var cmd = conn.CreateCommand())
{
conn.Open();
var databases = new Dictionary<string, string>();
SetCommand(cmd, @"
SELECT name, filename FROM sys.sysdatabases");
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
databases[reader.GetString(0)] = reader.GetString(1);
}
}
foreach (var database in databases)
{
var dbname = database.Key;
if (dbname == "master" || dbname == "tempdb" || dbname == "model" || dbname == "msdb")
continue;
// fixme - shall we deal with stale databases?
// fixme - is it always ok to assume file names?
//var mdf = database.Value;
//var ldf = mdf.Replace(".mdf", "_log.ldf");
//if (staleOnly && File.Exists(mdf) && File.Exists(ldf))
// continue;
//ExecuteDropDatabase(cmd, dbname, mdf, ldf);
//count++;
userDatabases.Add(dbname);
}
}
return userDatabases.ToArray();
}
/// <summary>
/// Gets a value indicating whether a database exists.
/// </summary>
/// <param name="databaseName">The name of the database.</param>
/// <returns>A value indicating whether a database with the specified name exists.</returns>
/// <remarks>
/// A database exists if it is registered in the instance, and its files exist. If the database
/// is registered but some of its files are missing, the database is dropped.
/// </remarks>
public bool DatabaseExists(string databaseName)
{
using (var conn = new SqlConnection(_masterCstr))
using (var cmd = conn.CreateCommand())
{
conn.Open();
var mdf = GetDatabase(cmd, databaseName);
if (mdf == null) return false;
// it can exist, even though its files have been deleted
// if files exist assume all is ok (should we try to connect?)
var ldf = GetLogFilename(mdf);
if (File.Exists(mdf) && File.Exists(ldf))
return true;
ExecuteDropDatabase(cmd, databaseName, mdf, ldf);
}
return false;
}
/// <summary>
/// Creates a new database.
/// </summary>
/// <param name="databaseName">The name of the database.</param>
/// <param name="filesPath">The directory containing database files.</param>
/// <returns>A value indicating whether the database was created without errors.</returns>
/// <remarks>
/// Failed if a database with the specified name already exists in the instance,
/// or if the database files already exist in the specified directory.
/// </remarks>
public bool CreateDatabase(string databaseName, string filesPath)
{
string logName, baseFilename, baseLogFilename, mdfFilename, ldfFilename;
GetDatabaseFiles(databaseName, filesPath, out logName, out baseFilename, out baseLogFilename, out mdfFilename, out ldfFilename);
using (var conn = new SqlConnection(_masterCstr))
using (var cmd = conn.CreateCommand())
{
conn.Open();
var mdf = GetDatabase(cmd, databaseName);
if (mdf != null) return false;
// cannot use parameters on CREATE DATABASE
// ie "CREATE DATABASE @0 ..." does not work
SetCommand(cmd, string.Format(@"
CREATE DATABASE {0}
ON (NAME=N{1}, FILENAME={2})
LOG ON (NAME=N{3}, FILENAME={4})",
QuotedName(databaseName),
QuotedName(databaseName, '\''), QuotedName(mdfFilename, '\''),
QuotedName(logName, '\''), QuotedName(ldfFilename, '\'')));
var unused = cmd.ExecuteNonQuery();
}
return true;
}
/// <summary>
/// Drops a database.
/// </summary>
/// <param name="databaseName">The name of the database.</param>
/// <returns>A value indicating whether the database was dropped without errors.</returns>
/// <remarks>
/// Successful if the database does not exist.
/// Deletes the database files.
/// </remarks>
public bool DropDatabase(string databaseName)
{
using (var conn = new SqlConnection(_masterCstr))
using (var cmd = conn.CreateCommand())
{
conn.Open();
SetCommand(cmd, @"
SELECT name, filename FROM master.dbo.sysdatabases WHERE ('[' + name + ']' = @0 OR name = @0)",
databaseName);
var mdf = GetDatabase(cmd, databaseName);
if (mdf == null) return true;
ExecuteDropDatabase(cmd, databaseName, mdf);
}
return true;
}
/// <summary>
/// Drops stale databases.
/// </summary>
/// <returns>The number of databases that were dropped.</returns>
/// <remarks>
/// A database is considered stale when its files cannot be found.
/// </remarks>
public int DropStaleDatabases()
{
return DropDatabases(true);
}
/// <summary>
/// Drops databases.
/// </summary>
/// <param name="staleOnly">A value indicating whether to delete only stale database.</param>
/// <returns>The number of databases that were dropped.</returns>
/// <remarks>
/// A database is considered stale when its files cannot be found.
/// </remarks>
public int DropDatabases(bool staleOnly = false)
{
var count = 0;
using (var conn = new SqlConnection(_masterCstr))
using (var cmd = conn.CreateCommand())
{
conn.Open();
var databases = new Dictionary<string, string>();
SetCommand(cmd, @"
SELECT name, filename FROM sys.sysdatabases");
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
databases[reader.GetString(0)] = reader.GetString(1);
}
}
foreach (var database in databases)
{
var dbname = database.Key;
if (dbname == "master" || dbname == "tempdb" || dbname == "model" || dbname == "msdb")
continue;
var mdf = database.Value;
var ldf = mdf.Replace(".mdf", "_log.ldf");
if (staleOnly && File.Exists(mdf) && File.Exists(ldf))
continue;
ExecuteDropDatabase(cmd, dbname, mdf, ldf);
count++;
}
}
return count;
}
/// <summary>
/// Detaches a database.
/// </summary>
/// <param name="databaseName">The name of the database.</param>
/// <returns>The directory containing the database files.</returns>
/// <exception cref="InvalidOperationException">Thrown when a database with the specified name does not exist.</exception>
public string DetachDatabase(string databaseName)
{
using (var conn = new SqlConnection(_masterCstr))
using (var cmd = conn.CreateCommand())
{
conn.Open();
var mdf = GetDatabase(cmd, databaseName);
if (mdf == null)
throw new InvalidOperationException("Database does not exist.");
DetachDatabase(cmd, databaseName);
return Path.GetDirectoryName(mdf);
}
}
/// <summary>
/// Attaches a database.
/// </summary>
/// <param name="databaseName">The name of the database.</param>
/// <param name="filesPath">The directory containing database files.</param>
/// <exception cref="InvalidOperationException">Thrown when a database with the specified name already exists.</exception>
public void AttachDatabase(string databaseName, string filesPath)
{
using (var conn = new SqlConnection(_masterCstr))
using (var cmd = conn.CreateCommand())
{
conn.Open();
var mdf = GetDatabase(cmd, databaseName);
if (mdf != null)
throw new InvalidOperationException("Database already exists.");
AttachDatabase(cmd, databaseName, filesPath);
}
}
/// <summary>
/// Gets the file names of a database.
/// </summary>
/// <param name="databaseName">The name of the database.</param>
/// <param name="mdfName">The MDF logical name.</param>
/// <param name="ldfName">The LDF logical name.</param>
/// <param name="mdfFilename">The MDF filename.</param>
/// <param name="ldfFilename">The LDF filename.</param>
public void GetFilenames(string databaseName,
out string mdfName, out string ldfName,
out string mdfFilename, out string ldfFilename)
{
using (var conn = new SqlConnection(_masterCstr))
using (var cmd = conn.CreateCommand())
{
conn.Open();
GetFilenames(cmd, databaseName, out mdfName, out ldfName, out mdfFilename, out ldfFilename);
}
}
/// <summary>
/// Kills all existing connections.
/// </summary>
/// <param name="databaseName">The name of the database.</param>
public void KillConnections(string databaseName)
{
using (var conn = new SqlConnection(_masterCstr))
using (var cmd = conn.CreateCommand())
{
conn.Open();
SetCommand(cmd, @"
DECLARE @sql VARCHAR(MAX);
SELECT @sql = COALESCE(@sql,'') + 'kill ' + CONVERT(VARCHAR, SPId) + ';'
FROM master.sys.sysprocesses
WHERE DBId = DB_ID(@0) AND SPId <> @@SPId;
EXEC(@sql);",
databaseName);
cmd.ExecuteNonQuery();
}
}
/// <summary>
/// Gets a database.
/// </summary>
/// <param name="cmd">The Sql Command.</param>
/// <param name="databaseName">The name of the database.</param>
/// <returns>The full filename of the MDF file, if the database exists, otherwise null.</returns>
private static string GetDatabase(SqlCommand cmd, string databaseName)
{
SetCommand(cmd, @"
SELECT name, filename FROM master.dbo.sysdatabases WHERE ('[' + name + ']' = @0 OR name = @0)",
databaseName);
string mdf = null;
using (var reader = cmd.ExecuteReader())
{
if (reader.Read())
mdf = reader.GetString(1) ?? string.Empty;
while (reader.Read())
{
}
}
return mdf;
}
/// <summary>
/// Drops a database and its files.
/// </summary>
/// <param name="cmd">The Sql command.</param>
/// <param name="databaseName">The name of the database.</param>
/// <param name="mdf">The name of the database (MDF) file.</param>
/// <param name="ldf">The name of the log (LDF) file.</param>
private static void ExecuteDropDatabase(SqlCommand cmd, string databaseName, string mdf, string ldf = null)
{
try
{
// cannot use parameters on ALTER DATABASE
// ie "ALTER DATABASE @0 ..." does not work
SetCommand(cmd, string.Format(@"
ALTER DATABASE {0} SET SINGLE_USER WITH ROLLBACK IMMEDIATE",
QuotedName(databaseName)));
var unused1 = cmd.ExecuteNonQuery();
}
catch (SqlException e)
{
if (e.Message.Contains("Unable to open the physical file") && e.Message.Contains("Operating system error 2:"))
{
// quite probably, the files were missing
// yet, it should be possible to drop the database anyways
// but we'll have to deal with the files
}
else
{
// no idea, throw
throw;
}
}
// cannot use parameters on DROP DATABASE
// ie "DROP DATABASE @0 ..." does not work
SetCommand(cmd, string.Format(@"
DROP DATABASE {0}",
QuotedName(databaseName)));
var unused2 = cmd.ExecuteNonQuery();
// be absolutely sure
if (File.Exists(mdf)) File.Delete(mdf);
ldf = ldf ?? GetLogFilename(mdf);
if (File.Exists(ldf)) File.Delete(ldf);
}
/// <summary>
/// Gets the log (LDF) filename corresponding to a database (MDF) filename.
/// </summary>
/// <param name="mdfFilename">The MDF filename.</param>
/// <returns></returns>
private static string GetLogFilename(string mdfFilename)
{
if (mdfFilename.EndsWith(".mdf") == false)
throw new ArgumentException("Not a valid MDF filename (no .mdf extension).", "mdfFilename");
return mdfFilename.Substring(0, mdfFilename.Length - ".mdf".Length) + "_log.ldf";
}
/// <summary>
/// Detaches a database.
/// </summary>
/// <param name="cmd">The Sql command.</param>
/// <param name="databaseName">The name of the database.</param>
private static void DetachDatabase(SqlCommand cmd, string databaseName)
{
// cannot use parameters on ALTER DATABASE
// ie "ALTER DATABASE @0 ..." does not work
SetCommand(cmd, string.Format(@"
ALTER DATABASE {0} SET SINGLE_USER WITH ROLLBACK IMMEDIATE",
QuotedName(databaseName)));
var unused1 = cmd.ExecuteNonQuery();
SetCommand(cmd, @"
EXEC sp_detach_db @dbname=@0",
databaseName);
var unused2 = cmd.ExecuteNonQuery();
}
/// <summary>
/// Attaches a database.
/// </summary>
/// <param name="cmd">The Sql command.</param>
/// <param name="databaseName">The name of the database.</param>
/// <param name="filesPath">The directory containing database files.</param>
private static void AttachDatabase(SqlCommand cmd, string databaseName, string filesPath)
{
string logName, baseFilename, baseLogFilename, mdfFilename, ldfFilename;
GetDatabaseFiles(databaseName, filesPath, out logName, out baseFilename, out baseLogFilename, out mdfFilename, out ldfFilename);
// cannot use parameters on CREATE DATABASE
// ie "CREATE DATABASE @0 ..." does not work
SetCommand(cmd, string.Format(@"
CREATE DATABASE {0}
ON (NAME=N{1}, FILENAME={2})
LOG ON (NAME=N{3}, FILENAME={4})
FOR ATTACH",
QuotedName(databaseName),
QuotedName(databaseName, '\''), QuotedName(mdfFilename, '\''),
QuotedName(logName, '\''), QuotedName(ldfFilename, '\'')));
var unused = cmd.ExecuteNonQuery();
}
/// <summary>
/// Sets a database command.
/// </summary>
/// <param name="cmd">The command.</param>
/// <param name="sql">The command text.</param>
/// <param name="args">The command arguments.</param>
/// <remarks>
/// The command text must refer to arguments as @0, @1... each referring
/// to the corresponding position in <paramref name="args"/>.
/// </remarks>
private static void SetCommand(SqlCommand cmd, string sql, params object[] args)
{
cmd.CommandType = CommandType.Text;
cmd.CommandText = sql;
cmd.Parameters.Clear();
for (var i = 0; i < args.Length; i++)
cmd.Parameters.AddWithValue("@" + i, args[i]);
}
/// <summary>
/// Gets the file names of a database.
/// </summary>
/// <param name="cmd">The Sql command.</param>
/// <param name="databaseName">The name of the database.</param>
/// <param name="mdfName">The MDF logical name.</param>
/// <param name="ldfName">The LDF logical name.</param>
/// <param name="mdfFilename">The MDF filename.</param>
/// <param name="ldfFilename">The LDF filename.</param>
private void GetFilenames(SqlCommand cmd, string databaseName,
out string mdfName, out string ldfName,
out string mdfFilename, out string ldfFilename)
{
mdfName = ldfName = mdfFilename = ldfFilename = null;
SetCommand(cmd, @"
SELECT DB_NAME(database_id), type_desc, name, physical_name
FROM master.sys.master_files
WHERE database_id=DB_ID(@0)",
databaseName);
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
var type = reader.GetString(1);
if (type == "ROWS")
{
mdfName = reader.GetString(2);
ldfName = reader.GetString(3);
}
else if (type == "LOG")
{
ldfName = reader.GetString(2);
ldfFilename = reader.GetString(3);
}
}
}
}
}
/// <summary>
/// Copy database files.
/// </summary>
/// <param name="databaseName">The name of the source database.</param>
/// <param name="filesPath">The directory containing source database files.</param>
/// <param name="targetDatabaseName">The name of the target database.</param>
/// <param name="targetFilesPath">The directory containing target database files.</param>
/// <param name="sourceExtension">The source database files extension.</param>
/// <param name="targetExtension">The target database files extension.</param>
/// <param name="overwrite">A value indicating whether to overwrite the target files.</param>
/// <param name="delete">A value indicating whether to delete the source files.</param>
/// <remarks>
/// The <paramref name="targetDatabaseName"/>, <paramref name="targetFilesPath"/>, <paramref name="sourceExtension"/>
/// and <paramref name="targetExtension"/> parameters are optional. If they result in target being identical
/// to source, no copy is performed. If <paramref name="delete"/> is false, nothing happens, otherwise the source
/// files are deleted.
/// If target is not identical to source, files are copied or moved, depending on the value of <paramref name="delete"/>.
/// Extensions are used eg to copy MyDatabase.mdf to MyDatabase.mdf.temp.
/// </remarks>
public void CopyDatabaseFiles(string databaseName, string filesPath,
string targetDatabaseName = null, string targetFilesPath = null,
string sourceExtension = null, string targetExtension = null,
bool overwrite = false, bool delete = false)
{
var nop = (targetFilesPath == null || targetFilesPath == filesPath)
&& (targetDatabaseName == null || targetDatabaseName == databaseName)
&& (sourceExtension == null && targetExtension == null || sourceExtension == targetExtension);
if (nop && delete == false) return;
string logName, baseFilename, baseLogFilename, mdfFilename, ldfFilename;
GetDatabaseFiles(databaseName, filesPath,
out logName, out baseFilename, out baseLogFilename, out mdfFilename, out ldfFilename);
if (sourceExtension != null)
{
mdfFilename += "." + sourceExtension;
ldfFilename += "." + sourceExtension;
}
if (nop)
{
// delete
if (File.Exists(mdfFilename)) File.Delete(mdfFilename);
if (File.Exists(ldfFilename)) File.Delete(ldfFilename);
}
else
{
// copy or copy+delete ie move
string targetLogName, targetBaseFilename, targetLogFilename, targetMdfFilename, targetLdfFilename;
GetDatabaseFiles(targetDatabaseName ?? databaseName, targetFilesPath ?? filesPath,
out targetLogName, out targetBaseFilename, out targetLogFilename, out targetMdfFilename, out targetLdfFilename);
if (targetExtension != null)
{
targetMdfFilename += "." + targetExtension;
targetLdfFilename += "." + targetExtension;
}
if (delete)
{
if (overwrite && File.Exists(targetMdfFilename)) File.Delete(targetMdfFilename);
if (overwrite && File.Exists(targetLdfFilename)) File.Delete(targetLdfFilename);
File.Move(mdfFilename, targetMdfFilename);
File.Move(ldfFilename, targetLdfFilename);
}
else
{
File.Copy(mdfFilename, targetMdfFilename, overwrite);
File.Copy(ldfFilename, targetLdfFilename, overwrite);
}
}
}
/// <summary>
/// Gets a value indicating whether database files exist.
/// </summary>
/// <param name="databaseName">The name of the source database.</param>
/// <param name="filesPath">The directory containing source database files.</param>
/// <param name="extension">The database files extension.</param>
/// <returns>A value indicating whether the database files exist.</returns>
/// <remarks>
/// Extensions are used eg to copy MyDatabase.mdf to MyDatabase.mdf.temp.
/// </remarks>
public bool DatabaseFilesExist(string databaseName, string filesPath, string extension = null)
{
string logName, baseFilename, baseLogFilename, mdfFilename, ldfFilename;
GetDatabaseFiles(databaseName, filesPath,
out logName, out baseFilename, out baseLogFilename, out mdfFilename, out ldfFilename);
if (extension != null)
{
mdfFilename += "." + extension;
ldfFilename += "." + extension;
}
return File.Exists(mdfFilename) && File.Exists(ldfFilename);
}
/// <summary>
/// Gets the name of the database files.
/// </summary>
/// <param name="databaseName">The name of the database.</param>
/// <param name="filesPath">The directory containing database files.</param>
/// <param name="logName">The name of the log.</param>
/// <param name="baseFilename">The base filename (the MDF filename without the .mdf extension).</param>
/// <param name="baseLogFilename">The base log filename (the LDF filename without the .ldf extension).</param>
/// <param name="mdfFilename">The MDF filename.</param>
/// <param name="ldfFilename">The LDF filename.</param>
private static void GetDatabaseFiles(string databaseName, string filesPath,
out string logName,
out string baseFilename, out string baseLogFilename,
out string mdfFilename, out string ldfFilename)
{
logName = databaseName + "_log";
baseFilename = Path.Combine(filesPath, databaseName);
baseLogFilename = Path.Combine(filesPath, logName);
mdfFilename = baseFilename + ".mdf";
ldfFilename = baseFilename + "_log.ldf";
}
#endregion
#region SqlLocalDB
/// <summary>
/// Executes the SqlLocalDB command.
/// </summary>
/// <param name="args">The arguments.</param>
/// <param name="output">The command standard output.</param>
/// <param name="error">The command error output.</param>
/// <returns>The process exit code.</returns>
/// <remarks>
/// Execution is successful if the exit code is zero, and error is empty.
/// </remarks>
private int ExecuteSqlLocalDb(string args, out string output, out string error)
{
var programFiles = Environment.GetEnvironmentVariable("ProgramFiles");
if (programFiles == null)
{
output = string.Empty;
error = "SqlLocalDB.exe not found";
return -1;
}
var path = Path.Combine(programFiles, string.Format(@"Microsoft SQL Server\{0}0\Tools\Binn\SqlLocalDB.exe", _version));
var p = new Process
{
StartInfo =
{
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
FileName = path,
Arguments = args,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden
}
};
p.Start();
output = p.StandardOutput.ReadToEnd();
error = p.StandardError.ReadToEnd();
p.WaitForExit();
return p.ExitCode;
}
/// <summary>
/// Returns a Unicode string with the delimiters added to make the input string a valid SQL Server delimited identifier.
/// </summary>
/// <param name="name">The name to quote.</param>
/// <param name="quote">A quote character.</param>
/// <returns></returns>
/// <remarks>
/// This is a C# implementation of T-SQL QUOTEDNAME.
/// <paramref name="quote"/> is optional, it can be '[' (default), ']', '\'' or '"'.
/// </remarks>
private static string QuotedName(string name, char quote = '[')
{
switch (quote)
{
case '[':
case ']':
return "[" + name.Replace("]", "]]") + "]";
case '\'':
return "'" + name.Replace("'", "''") + "'";
case '"':
return "\"" + name.Replace("\"", "\"\"") + "\"";
default:
throw new NotSupportedException("Not a valid quote character.");
}
}
#endregion
}
}
@@ -1,46 +0,0 @@
using System.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenTwoZero
{
[Migration("7.2.0", 3, Constants.System.UmbracoMigrationName)]
public class AddIndexToUmbracoNodeTable : MigrationBase
{
private readonly bool _skipIndexCheck;
internal AddIndexToUmbracoNodeTable(ISqlSyntaxProvider sqlSyntax, ILogger logger, bool skipIndexCheck) : base(sqlSyntax, logger)
{
_skipIndexCheck = skipIndexCheck;
}
public AddIndexToUmbracoNodeTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger)
{
}
public override void Up()
{
var dbIndexes = _skipIndexCheck ? new DbIndexDefinition[] { } : SqlSyntax.GetDefinedIndexes(Context.Database)
.Select(x => new DbIndexDefinition
{
TableName = x.Item1,
IndexName = x.Item2,
ColumnName = x.Item3,
IsUnique = x.Item4
}).ToArray();
//make sure it doesn't already exist
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoNodeUniqueID")) == false)
{
Create.Index("IX_umbracoNodeUniqueID").OnTable("umbracoNode").OnColumn("uniqueID").Ascending().WithOptions().NonClustered();
}
}
public override void Down()
{
Delete.Index("IX_umbracoNodeUniqueID").OnTable("umbracoNode");
}
}
}
@@ -435,7 +435,24 @@ namespace Umbraco.Core.Persistence.Repositories
nodeDto.Path = parent.Path;
nodeDto.Level = short.Parse(level.ToString(CultureInfo.InvariantCulture));
nodeDto.SortOrder = sortOrder;
var o = Database.IsNew(nodeDto) ? Convert.ToInt32(Database.Insert(nodeDto)) : Database.Update(nodeDto);
// note:
// there used to be a check on Database.IsNew(nodeDto) here to either Insert or Update,
// but I cannot figure out what was the point, as the node should obviously be new if
// we reach that point - removed.
// see if there's a reserved identifier for this unique id
var sql = new Sql("SELECT id FROM umbracoNode WHERE uniqueID=@0 AND nodeObjectType=@1", nodeDto.UniqueId, Constants.ObjectTypes.IdReservationGuid);
var id = Database.ExecuteScalar<int>(sql);
if (id > 0)
{
nodeDto.NodeId = id;
Database.Update(nodeDto);
}
else
{
Database.Insert(nodeDto);
}
//Update with new correct path
nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId);
@@ -1122,31 +1139,10 @@ ORDER BY cmsContentVersion.id DESC
if (EnsureUniqueNaming == false)
return nodeName;
var sql = new Sql();
sql.Select("*")
.From<NodeDto>()
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId && x.ParentId == parentId && x.Text.StartsWith(nodeName));
var names = Database.Fetch<SimilarNodeName>("SELECT id, text AS name FROM umbracoNode WHERE nodeObjectType=@objectType AND parentId=@parentId",
new { objectType = NodeObjectTypeId, parentId });
int uniqueNumber = 1;
var currentName = nodeName;
var dtos = Database.Fetch<NodeDto>(sql);
if (dtos.Any())
{
var results = dtos.OrderBy(x => x.Text, new SimilarNodeNameComparer());
foreach (var dto in results)
{
if (id != 0 && id == dto.NodeId) continue;
if (dto.Text.ToLowerInvariant().Equals(currentName.ToLowerInvariant()))
{
currentName = nodeName + string.Format(" ({0})", uniqueNumber);
uniqueNumber++;
}
}
}
return currentName;
return SimilarNodeName.GetUniqueName(names, id, nodeName);
}
/// <summary>
@@ -450,33 +450,10 @@ AND umbracoNode.id <> @id",
private string EnsureUniqueNodeName(string nodeName, int id = 0)
{
var names = Database.Fetch<SimilarNodeName>("SELECT id, text AS name FROM umbracoNode WHERE nodeObjectType=@objectType",
new { objectType = NodeObjectTypeId });
var sql = new Sql();
sql.Select("*")
.From<NodeDto>(SqlSyntax)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId && x.Text.StartsWith(nodeName));
int uniqueNumber = 1;
var currentName = nodeName;
var dtos = Database.Fetch<NodeDto>(sql);
if (dtos.Any())
{
var results = dtos.OrderBy(x => x.Text, new SimilarNodeNameComparer());
foreach (var dto in results)
{
if (id != 0 && id == dto.NodeId) continue;
if (dto.Text.ToLowerInvariant().Equals(currentName.ToLowerInvariant()))
{
currentName = nodeName + string.Format(" ({0})", uniqueNumber);
uniqueNumber++;
}
}
}
return currentName;
return SimilarNodeName.GetUniqueName(names, id, nodeName);
}
/// <summary>
@@ -260,6 +260,19 @@ namespace Umbraco.Core.Persistence.Repositories
return GetByQuery(query);
}
public Dictionary<string, Guid> GetDictionaryItemKeyMap()
{
var columns = new[] { "key", "id" }.Select(x => (object) SqlSyntax.GetQuotedColumnName(x)).ToArray();
var sql = new Sql().Select(columns).From<DictionaryDto>(SqlSyntax);
return Database.Fetch<DictionaryItemKeyIdDto>(sql).ToDictionary(x => x.Key, x => x.Id);
}
private class DictionaryItemKeyIdDto
{
public string Key { get; set; }
public Guid Id { get; set; }
}
public IEnumerable<IDictionaryItem> GetDictionaryItemDescendants(Guid? parentId)
{
//This methods will look up children at each level, since we do not store a path for dictionary (ATM), we need to do a recursive
@@ -9,5 +9,6 @@ namespace Umbraco.Core.Persistence.Repositories
IDictionaryItem Get(Guid uniqueId);
IDictionaryItem Get(string key);
IEnumerable<IDictionaryItem> GetDictionaryItemDescendants(Guid? parentId);
Dictionary<string, Guid> GetDictionaryItemKeyMap();
}
}
@@ -84,7 +84,7 @@ namespace Umbraco.Core.Persistence.Repositories
#endregion
#region Overrides of PetaPocoRepositoryBase<int,IMedia>
protected override Sql GetBaseQuery(BaseQueryType queryType)
{
var sql = new Sql();
@@ -153,7 +153,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// This is the underlying method that processes most queries for this repository
/// </summary>
/// <param name="sqlFull">
/// The full SQL to select all media data
/// The full SQL to select all media data
/// </param>
/// <param name="pagingSqlQuery">
/// The Id SQL to just return all media ids - used to process the properties for the media item
@@ -164,7 +164,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
// fetch returns a list so it's ok to iterate it in this method
var dtos = Database.Fetch<ContentVersionDto, ContentDto, NodeDto>(sqlFull);
//This is a tuple list identifying if the content item came from the cache or not
var content = new List<Tuple<IMedia, bool>>();
var defs = new DocumentDefinitionCollection();
@@ -180,7 +180,7 @@ namespace Umbraco.Core.Persistence.Repositories
if (withCache)
{
var cached = IsolatedCache.GetCacheItem<IMedia>(GetCacheIdKey<IMedia>(dto.NodeId));
//only use this cached version if the dto returned is the same version - this is just a safety check, media doesn't
//only use this cached version if the dto returned is the same version - this is just a safety check, media doesn't
//store different versions, but just in case someone corrupts some data we'll double check to be sure.
if (cached != null && cached.Version == dto.VersionId)
{
@@ -303,7 +303,7 @@ namespace Umbraco.Core.Persistence.Repositories
.From<ContentXmlDto>(SqlSyntax)
.InnerJoin<NodeDto>(SqlSyntax)
.On<ContentXmlDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId);
if (contentTypeIdsA.Length > 0)
{
xmlIdsQuery.InnerJoin<ContentDto>(SqlSyntax)
@@ -314,7 +314,7 @@ namespace Umbraco.Core.Persistence.Repositories
}
xmlIdsQuery.Where<NodeDto>(dto => dto.NodeObjectType == mediaObjectType, SqlSyntax);
var allXmlIds = Database.Fetch<int>(xmlIdsQuery);
var toRemove = allXmlIds.Except(allMediaIds).ToArray();
@@ -380,7 +380,24 @@ namespace Umbraco.Core.Persistence.Repositories
nodeDto.Path = parent.Path;
nodeDto.Level = short.Parse(level.ToString(CultureInfo.InvariantCulture));
nodeDto.SortOrder = sortOrder;
var o = Database.IsNew(nodeDto) ? Convert.ToInt32(Database.Insert(nodeDto)) : Database.Update(nodeDto);
// note:
// there used to be a check on Database.IsNew(nodeDto) here to either Insert or Update,
// but I cannot figure out what was the point, as the node should obviously be new if
// we reach that point - removed.
// see if there's a reserved identifier for this unique id
var sql = new Sql("SELECT id FROM umbracoNode WHERE uniqueID=@0 AND nodeObjectType=@1", nodeDto.UniqueId, Constants.ObjectTypes.IdReservationGuid);
var id = Database.ExecuteScalar<int>(sql);
if (id > 0)
{
nodeDto.NodeId = id;
Database.Update(nodeDto);
}
else
{
Database.Insert(nodeDto);
}
//Update with new correct path
nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId);
@@ -564,7 +581,7 @@ namespace Umbraco.Core.Persistence.Repositories
private IMedia CreateMediaFromDto(ContentVersionDto dto, Sql docSql)
{
var contentType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId);
var media = MediaFactory.BuildEntity(dto, contentType);
var docDef = new DocumentDefinition(dto, contentType);
@@ -584,31 +601,10 @@ namespace Umbraco.Core.Persistence.Repositories
if (EnsureUniqueNaming == false)
return nodeName;
var sql = new Sql();
sql.Select("*")
.From<NodeDto>()
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId && x.ParentId == parentId && x.Text.StartsWith(nodeName));
var names = Database.Fetch<SimilarNodeName>("SELECT id, text AS name FROM umbracoNode WHERE nodeObjectType=@objectType AND parentId=@parentId",
new { objectType = NodeObjectTypeId, parentId });
int uniqueNumber = 1;
var currentName = nodeName;
var dtos = Database.Fetch<NodeDto>(sql);
if (dtos.Any())
{
var results = dtos.OrderBy(x => x.Text, new SimilarNodeNameComparer());
foreach (var dto in results)
{
if (id != 0 && id == dto.NodeId) continue;
if (dto.Text.ToLowerInvariant().Equals(currentName.ToLowerInvariant()))
{
currentName = nodeName + string.Format(" ({0})", uniqueNumber);
uniqueNumber++;
}
}
}
return currentName;
return SimilarNodeName.GetUniqueName(names, id, nodeName);
}
/// <summary>
@@ -184,30 +184,16 @@ namespace Umbraco.Core.Persistence.Repositories
public IEnumerable<IMemberGroup> GetMemberGroupsForMember(string username)
{
//find the member by username
var memberSql = new Sql();
var memberObjectType = new Guid(Constants.ObjectTypes.Member);
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});
memberSql.Select("umbracoNode.id")
.From<NodeDto>()
.InnerJoin<MemberDto>()
.On<NodeDto, MemberDto>(dto => dto.NodeId, dto => dto.NodeId)
.Where<NodeDto>(x => x.NodeObjectType == memberObjectType)
.Where<MemberDto>(x => x.LoginName == username);
var memberIdUsername = Database.Fetch<int?>(memberSql).FirstOrDefault();
if (memberIdUsername.HasValue == false)
{
return Enumerable.Empty<IMemberGroup>();
}
var sql = new Sql();
sql.Select("umbracoNode.*")
.From<NodeDto>()
.InnerJoin<Member2MemberGroupDto>()
.On<NodeDto, Member2MemberGroupDto>(dto => dto.NodeId, dto => dto.MemberGroup)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId)
.Where<Member2MemberGroupDto>(x => x.Member == memberIdUsername.Value);
return Database.Fetch<NodeDto>(sql)
.DistinctBy(dto => dto.NodeId)
.Select(x => _modelFactory.BuildEntity(x));
@@ -4,6 +4,7 @@ using System.Globalization;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Microsoft.AspNet.Identity;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.EntityBase;
@@ -230,7 +231,19 @@ namespace Umbraco.Core.Persistence.Repositories
Database.Insert(dto.ContentVersionDto);
//Create the first entry in cmsMember
dto.NodeId = nodeDto.NodeId;
dto.NodeId = nodeDto.NodeId;
//if the password is empty, generate one with the special prefix
//this will hash the guid with a salt so should be nicely random
if (entity.RawPasswordValue.IsNullOrWhiteSpace())
{
var aspHasher = new PasswordHasher();
dto.Password = Constants.Security.EmptyPasswordPrefix +
aspHasher.HashPassword(Guid.NewGuid().ToString("N"));
//re-assign
entity.RawPasswordValue = dto.Password;
}
Database.Insert(dto);
//Create the PropertyData for this version - cmsPropertyData
@@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Umbraco.Core.Persistence.Repositories
{
internal class SimilarNodeName
{
private int _numPos = -2;
public int Id { get; set; }
public string Name { get; set; }
// cached - reused
public int NumPos
{
get
{
if (_numPos != -2) return _numPos;
var name = Name;
if (name[name.Length - 1] != ')')
return _numPos = -1;
var pos = name.LastIndexOf('(');
if (pos < 2 || pos == name.Length - 2) // < 2 and not < 0, because we want at least "x ("
return _numPos = -1;
return _numPos = pos;
}
}
// not cached - used only once
public int NumVal
{
get
{
if (NumPos < 0)
throw new InvalidOperationException();
int num;
if (int.TryParse(Name.Substring(NumPos + 1, Name.Length - 2 - NumPos), out num))
return num;
return 0;
}
}
// compare without allocating, nor parsing integers
internal class Comparer : IComparer<SimilarNodeName>
{
public int Compare(SimilarNodeName x, SimilarNodeName y)
{
if (x == null) throw new ArgumentNullException("x");
if (y == null) throw new ArgumentNullException("y");
var xpos = x.NumPos;
var ypos = y.NumPos;
var xname = x.Name;
var yname = y.Name;
if (xpos < 0 || ypos < 0 || xpos != ypos)
return string.Compare(xname, yname, StringComparison.Ordinal);
// compare the part before (number)
var n = string.Compare(xname, 0, yname, 0, xpos, StringComparison.Ordinal);
if (n != 0)
return n;
// compare (number) lengths
var diff = xname.Length - yname.Length;
if (diff != 0) return diff < 0 ? -1 : +1;
// actually compare (number)
var i = xpos;
while (i < xname.Length - 1)
{
if (xname[i] != yname[i])
return xname[i] < yname[i] ? -1 : +1;
i++;
}
return 0;
}
}
// gets a unique name
public static string GetUniqueName(IEnumerable<SimilarNodeName> names, int nodeId, string nodeName)
{
var uniqueNumber = 1;
var uniqueing = false;
foreach (var name in names.OrderBy(x => x, new Comparer()))
{
// ignore self
if (nodeId != 0 && name.Id == nodeId) continue;
if (uniqueing)
{
if (name.NumPos > 0 && name.Name.StartsWith(nodeName) && name.NumVal == uniqueNumber)
uniqueNumber++;
else
break;
}
else if (name.Name.InvariantEquals(nodeName))
{
uniqueing = true;
}
}
return uniqueing ? string.Concat(nodeName, " (", uniqueNumber.ToString(), ")") : nodeName;
}
}
}
@@ -1,45 +0,0 @@
using System;
using System.Collections.Generic;
namespace Umbraco.Core.Persistence.Repositories
{
/// <summary>
/// Comparer that takes into account the duplicate index of a node name
/// This is needed as a normal alphabetic sort would go Page (1), Page (10), Page (2) etc.
/// </summary>
internal class SimilarNodeNameComparer : IComparer<string>
{
public int Compare(string x, string y)
{
if (x.LastIndexOf('(') != -1 && x.LastIndexOf(')') == x.Length - 1 && y.LastIndexOf(')') == y.Length - 1)
{
if (x.ToLower().Substring(0, x.LastIndexOf('(')) == y.ToLower().Substring(0, y.LastIndexOf('(')))
{
int xDuplicateIndex = ExtractDuplicateIndex(x);
int yDuplicateIndex = ExtractDuplicateIndex(y);
if (xDuplicateIndex != 0 && yDuplicateIndex != 0)
{
return xDuplicateIndex.CompareTo(yDuplicateIndex);
}
}
}
return String.Compare(x.ToLower(), y.ToLower(), StringComparison.Ordinal);
}
private int ExtractDuplicateIndex(string text)
{
int index = 0;
if (text.LastIndexOf('(') != -1 && text.LastIndexOf('(') < text.Length - 2)
{
int startPos = text.LastIndexOf('(') + 1;
int length = text.Length - 1 - startPos;
int.TryParse(text.Substring(startPos, length), out index);
}
return index;
}
}
}
+51 -60
View File
@@ -44,8 +44,8 @@ namespace Umbraco.Core
private readonly object _typesLock = new object();
private readonly Dictionary<TypeListKey, TypeList> _types = new Dictionary<TypeListKey, TypeList>();
private long _cachedAssembliesHash = -1;
private long _currentAssembliesHash = -1;
private string _cachedAssembliesHash = null;
private string _currentAssembliesHash = null;
private IEnumerable<Assembly> _assemblies;
private bool _reportedChange;
@@ -75,9 +75,9 @@ namespace Umbraco.Core
if (detectChanges)
{
//first check if the cached hash is 0, if it is then we ne
//first check if the cached hash is string.Empty, if it is then we need
//do the check if they've changed
RequiresRescanning = (CachedAssembliesHash != CurrentAssembliesHash) || CachedAssembliesHash == 0;
RequiresRescanning = (CachedAssembliesHash != CurrentAssembliesHash) || CachedAssembliesHash == string.Empty;
//if they have changed, we need to write the new file
if (RequiresRescanning)
{
@@ -180,23 +180,20 @@ namespace Umbraco.Core
/// <summary>
/// Gets the currently cached hash value of the scanned assemblies.
/// </summary>
/// <value>The cached hash value, or 0 if no cache is found.</value>
internal long CachedAssembliesHash
/// <value>The cached hash value, or string.Empty if no cache is found.</value>
internal string CachedAssembliesHash
{
get
{
if (_cachedAssembliesHash != -1)
if (_cachedAssembliesHash != null)
return _cachedAssembliesHash;
var filePath = GetPluginHashFilePath();
if (File.Exists(filePath) == false) return 0;
if (File.Exists(filePath) == false) return string.Empty;
var hash = File.ReadAllText(filePath, Encoding.UTF8);
long val;
if (long.TryParse(hash, out val) == false) return 0;
_cachedAssembliesHash = val;
_cachedAssembliesHash = hash;
return _cachedAssembliesHash;
}
}
@@ -205,11 +202,11 @@ namespace Umbraco.Core
/// Gets the current assemblies hash based on creating a hash from the assemblies in various places.
/// </summary>
/// <value>The current hash.</value>
internal long CurrentAssembliesHash
internal string CurrentAssembliesHash
{
get
{
if (_currentAssembliesHash != -1)
if (_currentAssembliesHash != null)
return _currentAssembliesHash;
_currentAssembliesHash = GetFileHash(new List<Tuple<FileSystemInfo, bool>>
@@ -245,41 +242,40 @@ namespace Umbraco.Core
/// <returns>The hash.</returns>
/// <remarks>Each file is a tuple containing the FileInfo object and a boolean which indicates whether to hash the
/// file properties (false) or the file contents (true).</remarks>
internal static long GetFileHash(IEnumerable<Tuple<FileSystemInfo, bool>> filesAndFolders, ProfilingLogger logger)
internal static string GetFileHash(IEnumerable<Tuple<FileSystemInfo, bool>> filesAndFolders, ProfilingLogger logger)
{
using (logger.TraceDuration<PluginManager>("Determining hash of code files on disk", "Hash determined"))
{
var hashCombiner = new HashCodeCombiner();
// get the distinct file infos to hash
var uniqInfos = new HashSet<string>();
var uniqContent = new HashSet<string>();
foreach (var fileOrFolder in filesAndFolders)
using (var generator = new HashGenerator())
{
var info = fileOrFolder.Item1;
if (fileOrFolder.Item2)
foreach (var fileOrFolder in filesAndFolders)
{
// add each unique file's contents to the hash
// normalize the content for cr/lf and case-sensitivity
if (uniqContent.Contains(info.FullName)) continue;
uniqContent.Add(info.FullName);
if (File.Exists(info.FullName) == false) continue;
var content = RemoveCrLf(File.ReadAllText(info.FullName));
hashCombiner.AddCaseInsensitiveString(content);
}
else
{
// add each unique folder/file to the hash
if (uniqInfos.Contains(info.FullName)) continue;
uniqInfos.Add(info.FullName);
hashCombiner.AddFileSystemItem(info);
var info = fileOrFolder.Item1;
if (fileOrFolder.Item2)
{
// add each unique file's contents to the hash
// normalize the content for cr/lf and case-sensitivity
if (uniqContent.Add(info.FullName))
{
if (File.Exists(info.FullName) == false) continue;
var content = RemoveCrLf(File.ReadAllText(info.FullName));
generator.AddCaseInsensitiveString(content);
}
}
else
{
// add each unique folder/file to the hash
if (uniqInfos.Add(info.FullName))
{
generator.AddFileSystemItem(info);
}
}
}
return generator.GenerateHash();
}
return ConvertHashToInt64(hashCombiner.GetCombinedHashCode());
}
}
@@ -303,35 +299,26 @@ namespace Umbraco.Core
/// <param name="filesAndFolders">A collection of files.</param>
/// <param name="logger">A profiling logger.</param>
/// <returns>The hash.</returns>
internal static long GetFileHash(IEnumerable<FileSystemInfo> filesAndFolders, ProfilingLogger logger)
internal static string GetFileHash(IEnumerable<FileSystemInfo> filesAndFolders, ProfilingLogger logger)
{
using (logger.TraceDuration<PluginManager>("Determining hash of code files on disk", "Hash determined"))
{
var hashCombiner = new HashCodeCombiner();
// get the distinct file infos to hash
var uniqInfos = new HashSet<string>();
foreach (var fileOrFolder in filesAndFolders)
using (var generator = new HashGenerator())
{
if (uniqInfos.Contains(fileOrFolder.FullName)) continue;
uniqInfos.Add(fileOrFolder.FullName);
hashCombiner.AddFileSystemItem(fileOrFolder);
// get the distinct file infos to hash
var uniqInfos = new HashSet<string>();
foreach (var fileOrFolder in filesAndFolders)
{
if (uniqInfos.Contains(fileOrFolder.FullName)) continue;
uniqInfos.Add(fileOrFolder.FullName);
generator.AddFileSystemItem(fileOrFolder);
}
return generator.GenerateHash();
}
return ConvertHashToInt64(hashCombiner.GetCombinedHashCode());
}
}
/// <summary>
/// Converts a string hash value into an Int64.
/// </summary>
internal static long ConvertHashToInt64(string val)
{
long outVal;
return long.TryParse(val, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out outVal) ? outVal : 0;
}
#endregion
#region Cache
@@ -452,6 +439,10 @@ namespace Umbraco.Core
internal void WriteCache()
{
// be absolutely sure
if (Directory.Exists(_tempFolder) == false)
Directory.CreateDirectory(_tempFolder);
var filePath = GetPluginListFilePath();
using (var stream = GetFileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None, ListFileOpenWriteTimeout))
@@ -293,7 +293,7 @@ namespace Umbraco.Core.PropertyEditors
//swallow this exception, we thought it was json but it really isn't so continue returning a string
}
}
return property.Value.ToString();
return asString;
case DataTypeDatabaseType.Integer:
case DataTypeDatabaseType.Decimal:
//Decimals need to be formatted with invariant culture (dots, not commas)
@@ -53,7 +53,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
// Fall back on normal behaviour
if (values.Any() == false)
{
return sourceString.Split(Environment.NewLine.ToCharArray());
return sourceString.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
}
return values.ToArray();
@@ -49,7 +49,7 @@ namespace Umbraco.Core.Security
public override async Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool isPersistent, bool shouldLockout)
{
var result = await PasswordSignInAsyncImpl(userName, password, isPersistent, shouldLockout);
switch (result)
{
case SignInStatus.Success:
@@ -104,6 +104,14 @@ namespace Umbraco.Core.Security
var user = await UserManager.FindByNameAsync(userName);
if (user == null)
{
var requestContext = _request.Context;
if (requestContext != null)
{
var backofficeUserManager = requestContext.GetBackOfficeUserManager();
if (backofficeUserManager != null)
backofficeUserManager.RaiseInvalidLoginAttemptEvent(userName);
}
return SignInStatus.Failure;
}
if (await UserManager.IsLockedOutAsync(user.Id))
@@ -121,6 +129,15 @@ namespace Umbraco.Core.Security
await UserManager.AccessFailedAsync(user.Id);
if (await UserManager.IsLockedOutAsync(user.Id))
{
//at this point we've just locked the user out after too many failed login attempts
var requestContext = _request.Context;
if (requestContext != null)
{
var backofficeUserManager = requestContext.GetBackOfficeUserManager();
if (backofficeUserManager != null)
backofficeUserManager.RaiseAccountLockedEvent(user.Id);
}
return SignInStatus.LockedOut;
}
}
@@ -176,7 +193,7 @@ namespace Umbraco.Core.Security
AllowRefresh = true,
IssuedUtc = nowUtc,
ExpiresUtc = nowUtc.AddMinutes(GlobalSettings.TimeOutInMinutes)
}, userIdentity, rememberBrowserIdentity);
}, userIdentity, rememberBrowserIdentity);
}
else
{
@@ -191,7 +208,9 @@ namespace Umbraco.Core.Security
//track the last login date
user.LastLoginDateUtc = DateTime.UtcNow;
user.AccessFailedCount = 0;
if (user.AccessFailedCount > 0)
//we have successfully logged in, reset the AccessFailedCount
user.AccessFailedCount = 0;
await UserManager.UpdateAsync(user);
_logger.WriteCore(TraceEventType.Information, 0,
@@ -232,4 +251,4 @@ namespace Umbraco.Core.Security
return null;
}
}
}
}
@@ -1,11 +1,11 @@
using System;
using System.Linq;
using System.Text;
using System.Configuration.Provider;
using System.Threading.Tasks;
using System.Web.Security;
using System.Web;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security.DataProtection;
using Umbraco.Core.Auditing;
using Umbraco.Core.Models.Identity;
using Umbraco.Core.Services;
@@ -29,7 +29,7 @@ namespace Umbraco.Core.Security
MembershipProviderBase membershipProvider)
: base(store)
{
if (options == null) throw new ArgumentNullException("options");;
if (options == null) throw new ArgumentNullException("options");
InitUserManager(this, membershipProvider, options);
}
@@ -140,8 +140,8 @@ namespace Umbraco.Core.Security
/// <param name="dataProtectionProvider"></param>
/// <returns></returns>
protected void InitUserManager(
BackOfficeUserManager<T> manager,
MembershipProviderBase membershipProvider,
BackOfficeUserManager<T> manager,
MembershipProviderBase membershipProvider,
IDataProtectionProvider dataProtectionProvider)
{
// Configure validation logic for usernames
@@ -164,7 +164,7 @@ namespace Umbraco.Core.Security
//use a custom hasher based on our membership provider
manager.PasswordHasher = new MembershipPasswordHasher(membershipProvider);
if (dataProtectionProvider != null)
{
manager.UserTokenProvider = new DataProtectorTokenProvider<T, int>(dataProtectionProvider.Create("ASP.NET Identity"));
@@ -238,5 +238,192 @@ namespace Umbraco.Core.Security
/// Gets/sets the default back office user password checker
/// </summary>
public IBackOfficeUserPasswordChecker BackOfficeUserPasswordChecker { get; set; }
public override Task<IdentityResult> SetLockoutEndDateAsync(int userId, DateTimeOffset lockoutEnd)
{
var result = base.SetLockoutEndDateAsync(userId, lockoutEnd);
// The way we unlock is by setting the lockoutEnd date to the current datetime
if (result.Result.Succeeded && lockoutEnd >= DateTimeOffset.UtcNow)
RaiseAccountLockedEvent(userId);
else
RaiseAccountUnlockedEvent(userId);
return result;
}
public override Task<IdentityResult> AccessFailedAsync(int userId)
{
var result = base.AccessFailedAsync(userId);
//Slightly confusing: this will return a Success if we successfully update the AccessFailed count
if (result.Result.Succeeded)
RaiseLoginFailedEvent(userId);
return result;
}
public override Task<IdentityResult> ChangePasswordAsync(int userId, string currentPassword, string newPassword)
{
var result = base.ChangePasswordAsync(userId, currentPassword, newPassword);
if (result.Result.Succeeded)
RaisePasswordChangedEvent(userId);
return result;
}
public override async Task<IdentityResult> ResetAccessFailedCountAsync(int userId)
{
var lockoutStore = (IUserLockoutStore<BackOfficeIdentityUser, int>)Store;
var user = await FindByIdAsync(userId);
if (user == null)
throw new InvalidOperationException("No user found by user id " + userId);
var accessFailedCount = await GetAccessFailedCountAsync(user.Id);
if (accessFailedCount == 0)
return IdentityResult.Success;
await lockoutStore.ResetAccessFailedCountAsync(user);
//raise the event now that it's reset
RaiseResetAccessFailedCountEvent(userId);
return await UpdateAsync(user);
}
internal void RaiseAccountLockedEvent(int userId)
{
OnAccountLocked(new IdentityAuditEventArgs(AuditEvent.AccountLocked, GetCurrentRequestIpAddress(), userId));
}
internal void RaiseAccountUnlockedEvent(int userId)
{
OnAccountUnlocked(new IdentityAuditEventArgs(AuditEvent.AccountUnlocked, GetCurrentRequestIpAddress(), userId));
}
internal void RaiseForgotPasswordRequestedEvent(int userId)
{
OnForgotPasswordRequested(new IdentityAuditEventArgs(AuditEvent.ForgotPasswordRequested, GetCurrentRequestIpAddress(), userId));
}
internal void RaiseForgotPasswordChangedSuccessEvent(int userId)
{
OnForgotPasswordChangedSuccess(new IdentityAuditEventArgs(AuditEvent.ForgotPasswordChangedSuccess, GetCurrentRequestIpAddress(), userId));
}
internal void RaiseLoginFailedEvent(int userId)
{
OnLoginFailed(new IdentityAuditEventArgs(AuditEvent.LoginFailed, GetCurrentRequestIpAddress(), userId));
}
internal void RaiseInvalidLoginAttemptEvent(string username)
{
OnLoginFailed(new IdentityAuditEventArgs(AuditEvent.LoginFailed, GetCurrentRequestIpAddress(), username, string.Format("Attempted login for username '{0}' failed", username)));
}
internal void RaiseLoginRequiresVerificationEvent(int userId)
{
OnLoginRequiresVerification(new IdentityAuditEventArgs(AuditEvent.LoginRequiresVerification, GetCurrentRequestIpAddress(), userId));
}
internal void RaiseLoginSuccessEvent(int userId)
{
OnLoginSuccess(new IdentityAuditEventArgs(AuditEvent.LoginSucces, GetCurrentRequestIpAddress(), userId));
}
internal void RaiseLogoutSuccessEvent(int userId)
{
OnLogoutSuccess(new IdentityAuditEventArgs(AuditEvent.LogoutSuccess, GetCurrentRequestIpAddress(), userId));
}
internal void RaisePasswordChangedEvent(int userId)
{
OnPasswordChanged(new IdentityAuditEventArgs(AuditEvent.PasswordChanged, GetCurrentRequestIpAddress(), userId));
}
internal void RaisePasswordResetEvent(int userId)
{
OnPasswordReset(new IdentityAuditEventArgs(AuditEvent.PasswordReset, GetCurrentRequestIpAddress(), userId));
}
internal void RaiseResetAccessFailedCountEvent(int userId)
{
OnResetAccessFailedCount(new IdentityAuditEventArgs(AuditEvent.ResetAccessFailedCount, GetCurrentRequestIpAddress(), userId));
}
public static event EventHandler AccountLocked;
public static event EventHandler AccountUnlocked;
public static event EventHandler ForgotPasswordRequested;
public static event EventHandler ForgotPasswordChangedSuccess;
public static event EventHandler LoginFailed;
public static event EventHandler LoginRequiresVerification;
public static event EventHandler LoginSuccess;
public static event EventHandler LogoutSuccess;
public static event EventHandler PasswordChanged;
public static event EventHandler PasswordReset;
public static event EventHandler ResetAccessFailedCount;
protected virtual void OnAccountLocked(IdentityAuditEventArgs e)
{
if (AccountLocked != null) AccountLocked(this, e);
}
protected virtual void OnAccountUnlocked(IdentityAuditEventArgs e)
{
if (AccountUnlocked != null) AccountUnlocked(this, e);
}
protected virtual void OnForgotPasswordRequested(IdentityAuditEventArgs e)
{
if (ForgotPasswordRequested != null) ForgotPasswordRequested(this, e);
}
protected virtual void OnForgotPasswordChangedSuccess(IdentityAuditEventArgs e)
{
if (ForgotPasswordChangedSuccess != null) ForgotPasswordChangedSuccess(this, e);
}
protected virtual void OnLoginFailed(IdentityAuditEventArgs e)
{
if (LoginFailed != null) LoginFailed(this, e);
}
protected virtual void OnLoginRequiresVerification(IdentityAuditEventArgs e)
{
if (LoginRequiresVerification != null) LoginRequiresVerification(this, e);
}
protected virtual void OnLoginSuccess(IdentityAuditEventArgs e)
{
if (LoginSuccess != null) LoginSuccess(this, e);
}
protected virtual void OnLogoutSuccess(IdentityAuditEventArgs e)
{
if (LogoutSuccess != null) LogoutSuccess(this, e);
}
protected virtual void OnPasswordChanged(IdentityAuditEventArgs e)
{
if (PasswordChanged != null) PasswordChanged(this, e);
}
protected virtual void OnPasswordReset(IdentityAuditEventArgs e)
{
if (PasswordReset != null) PasswordReset(this, e);
}
protected virtual void OnResetAccessFailedCount(IdentityAuditEventArgs e)
{
if (ResetAccessFailedCount != null) ResetAccessFailedCount(this, e);
}
/// <summary>
/// Returns the current request IP address for logging if there is one
/// </summary>
/// <returns></returns>
protected virtual string GetCurrentRequestIpAddress()
{
//TODO: inject a service to get this value, we should not be relying on the old HttpContext.Current especially in the ASP.NET Identity world.
var httpContext = HttpContext.Current == null ? (HttpContextBase)null : new HttpContextWrapper(HttpContext.Current);
return httpContext.GetCurrentRequestIpAddress();
}
}
}
@@ -93,7 +93,7 @@ namespace Umbraco.Core.Security
{
//this will hash the guid with a salt so should be nicely random
var aspHasher = new PasswordHasher();
member.RawPasswordValue = "___UIDEMPTYPWORD__" +
member.RawPasswordValue = Constants.Security.EmptyPasswordPrefix +
aspHasher.HashPassword(Guid.NewGuid().ToString("N"));
}
@@ -64,6 +64,19 @@ namespace Umbraco.Core.Security
{
get { return false; }
}
/// <summary>
/// Returns the raw password value for a given user
/// </summary>
/// <param name="username"></param>
/// <returns></returns>
/// <remarks>
/// By default this will return an invalid attempt, inheritors will need to override this to support it
/// </remarks>
protected virtual Attempt<string> GetRawPassword(string username)
{
return Attempt<string>.Fail();
}
private string _applicationName;
private bool _enablePasswordReset;
@@ -299,7 +312,7 @@ namespace Umbraco.Core.Security
/// Processes a request to update the password for a membership user.
/// </summary>
/// <param name="username">The user to update the password for.</param>
/// <param name="oldPassword">This property is ignore for this provider</param>
/// <param name="oldPassword">Required to change a user password if the user is not new and AllowManuallyChangingPassword is false</param>
/// <param name="newPassword">The new password for the specified user.</param>
/// <returns>
/// true if the password was updated successfully; otherwise, false.
@@ -309,10 +322,17 @@ namespace Umbraco.Core.Security
/// </remarks>
public override bool ChangePassword(string username, string oldPassword, string newPassword)
{
string rawPasswordValue = string.Empty;
if (oldPassword.IsNullOrWhiteSpace() && AllowManuallyChangingPassword == false)
{
//If the old password is empty and AllowManuallyChangingPassword is false, than this provider cannot just arbitrarily change the password
throw new NotSupportedException("This provider does not support manually changing the password");
{
//we need to lookup the member since this could be a brand new member without a password set
var rawPassword = GetRawPassword(username);
rawPasswordValue = rawPassword.Success ? rawPassword.Result : string.Empty;
if (rawPassword.Success == false || rawPasswordValue.StartsWith(Constants.Security.EmptyPasswordPrefix) == false)
{
//If the old password is empty and AllowManuallyChangingPassword is false, than this provider cannot just arbitrarily change the password
throw new NotSupportedException("This provider does not support manually changing the password");
}
}
var args = new ValidatePasswordEventArgs(username, newPassword, false);
@@ -325,10 +345,14 @@ namespace Umbraco.Core.Security
throw new MembershipPasswordException("Change password canceled due to password validation failure.");
}
//Special case to allow changing password without validating existing credentials
//This is used during installation only
if (AllowManuallyChangingPassword == false && ApplicationContext.Current != null
&& ApplicationContext.Current.IsConfigured == false && oldPassword == "default")
//Special cases to allow changing password without validating existing credentials
// * the member is new and doesn't have a password set
// * during installation to set the admin password
if (AllowManuallyChangingPassword == false
&& (rawPasswordValue.StartsWith(Constants.Security.EmptyPasswordPrefix)
|| (ApplicationContext.Current != null
&& ApplicationContext.Current.IsConfigured == false
&& oldPassword == "default")))
{
return PerformChangePassword(username, oldPassword, newPassword);
}
@@ -0,0 +1,26 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Umbraco.Core.Serialization
{
public class KnownTypeUdiJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(Udi).IsAssignableFrom(objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jo = JToken.ReadFrom(reader);
var val = jo.ToObject<string>();
return val == null ? null : Udi.Parse(val, true);
}
}
}
@@ -4,12 +4,11 @@ using Newtonsoft.Json.Linq;
namespace Umbraco.Core.Serialization
{
public class UdiJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(Udi).IsAssignableFrom(objectType);
return typeof (Udi).IsAssignableFrom(objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+120 -88
View File
@@ -13,13 +13,11 @@ using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Publishing;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.Services
{
@@ -166,7 +164,8 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(Creating, this, new NewEventArgs<IContent>(content, contentTypeAlias, parentId)))
var newEventArgs = new NewEventArgs<IContent>(content, contentTypeAlias, parentId);
if (uow.Events.DispatchCancelable(Creating, this, newEventArgs))
{
uow.Commit();
content.WasCancelled = true;
@@ -175,8 +174,8 @@ namespace Umbraco.Core.Services
content.CreatorId = userId;
content.WriterId = userId;
uow.Events.Dispatch(Created, this, new NewEventArgs<IContent>(content, false, contentTypeAlias, parentId));
newEventArgs.CanCancel = false;
uow.Events.Dispatch(Created, this, newEventArgs);
Audit(uow, AuditType.New, string.Format("Content '{0}' was created", name), content.CreatorId, content.Id);
uow.Commit();
@@ -209,7 +208,8 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(Creating, this, new NewEventArgs<IContent>(content, contentTypeAlias, parent)))
var newEventArgs = new NewEventArgs<IContent>(content, contentTypeAlias, parent);
if (uow.Events.DispatchCancelable(Creating, this, newEventArgs))
{
uow.Commit();
content.WasCancelled = true;
@@ -218,8 +218,8 @@ namespace Umbraco.Core.Services
content.CreatorId = userId;
content.WriterId = userId;
uow.Events.Dispatch(Created, this, new NewEventArgs<IContent>(content, false, contentTypeAlias, parent));
newEventArgs.CanCancel = false;
uow.Events.Dispatch(Created, this, newEventArgs);
Audit(uow, AuditType.New, string.Format("Content '{0}' was created", name), content.CreatorId, content.Id);
uow.Commit();
@@ -250,14 +250,16 @@ namespace Umbraco.Core.Services
{
//NOTE: I really hate the notion of these Creating/Created events - they are so inconsistent, I've only just found
// out that in these 'WithIdentity' methods, the Saving/Saved events were not fired, wtf. Anyways, they're added now.
if (uow.Events.DispatchCancelable(Creating, this, new NewEventArgs<IContent>(content, contentTypeAlias, parentId)))
var newEventArgs = new NewEventArgs<IContent>(content, contentTypeAlias, parentId);
if (uow.Events.DispatchCancelable(Creating, this, newEventArgs))
{
uow.Commit();
content.WasCancelled = true;
return content;
}
if (uow.Events.DispatchCancelable(Saving, this, new SaveEventArgs<IContent>(content)))
var saveEventArgs = new SaveEventArgs<IContent>(content);
if (uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
{
uow.Commit();
content.WasCancelled = true;
@@ -270,9 +272,10 @@ namespace Umbraco.Core.Services
repository.AddOrUpdate(content);
repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, _userService, c));
uow.Events.Dispatch(Saved, this, new SaveEventArgs<IContent>(content, false));
uow.Events.Dispatch(Created, this, new NewEventArgs<IContent>(content, false, contentTypeAlias, parentId));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
newEventArgs.CanCancel = false;
uow.Events.Dispatch(Created, this, newEventArgs);
Audit(uow, AuditType.New, string.Format("Content '{0}' was created with Id {1}", name, content.Id), content.CreatorId, content.Id);
uow.Commit();
@@ -305,14 +308,16 @@ namespace Umbraco.Core.Services
{
//NOTE: I really hate the notion of these Creating/Created events - they are so inconsistent, I've only just found
// out that in these 'WithIdentity' methods, the Saving/Saved events were not fired, wtf. Anyways, they're added now.
if (uow.Events.DispatchCancelable(Creating, this, new NewEventArgs<IContent>(content, contentTypeAlias, parent)))
var newEventArgs = new NewEventArgs<IContent>(content, contentTypeAlias, parent);
if (uow.Events.DispatchCancelable(Creating, this, newEventArgs))
{
uow.Commit();
content.WasCancelled = true;
return content;
}
if (uow.Events.DispatchCancelable(Saving, this, new SaveEventArgs<IContent>(content)))
var saveEventArgs = new SaveEventArgs<IContent>(content);
if (uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
{
uow.Commit();
content.WasCancelled = true;
@@ -325,9 +330,10 @@ namespace Umbraco.Core.Services
repository.AddOrUpdate(content);
repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, _userService, c));
uow.Events.Dispatch(Saved, this, new SaveEventArgs<IContent>(content, false));
uow.Events.Dispatch(Created, this, new NewEventArgs<IContent>(content, false, contentTypeAlias, parent));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
newEventArgs.CanCancel = false;
uow.Events.Dispatch(Created, this, newEventArgs);
Audit(uow, AuditType.New, string.Format("Content '{0}' was created with Id {1}", name, content.Id), content.CreatorId, content.Id);
uow.Commit();
@@ -895,15 +901,43 @@ namespace Umbraco.Core.Services
/// <returns>True if the Content can be published, otherwise False</returns>
public bool IsPublishable(IContent content)
{
//If the passed in content has yet to be saved we "fallback" to checking the Parent
//because if the Parent is publishable then the current content can be Saved and Published
if (content.HasIdentity == false)
int[] ids;
if (content.HasIdentity)
{
var parent = GetById(content.ParentId);
return IsPublishable(parent, true);
// get ids from path (we have identity)
// skip the first one that has to be -1 - and we don't care
// skip the last one that has to be "this" - and it's ok to stop at the parent
ids = content.Path.Split(',').Skip(1).SkipLast().Select(int.Parse).ToArray();
}
else
{
// no path yet (no identity), have to move up to parent
// skip the first one that has to be -1 - and we don't care
// don't skip the last one that is "parent"
var parent = GetById(content.ParentId);
if (parent == null) return false;
ids = parent.Path.Split(',').Skip(1).Select(int.Parse).ToArray();
}
if (ids.Length == 0)
return false;
return IsPublishable(content, false);
// if the first one is recycle bin, fail fast
if (ids[0] == Constants.System.RecycleBinContent)
return false;
// fixme - move to repository?
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
{
var sql = new Sql(@"
SELECT id
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);
var x = uow.Database.Fetch<int>(sql);
return ids.Length == x.Count;
}
}
/// <summary>
@@ -1018,14 +1052,16 @@ namespace Umbraco.Core.Services
//see: http://issues.umbraco.org/issue/U4-9336
content.EnsureValidPath(Logger, entity => GetById(entity.ParentId), QuickUpdate);
var originalPath = content.Path;
if (uow.Events.DispatchCancelable(Trashing, this, new MoveEventArgs<IContent>(evtMsgs, new MoveEventInfo<IContent>(content, originalPath, Constants.System.RecycleBinContent)), "Trashing"))
var moveEventInfo = new MoveEventInfo<IContent>(content, originalPath, Constants.System.RecycleBinContent);
var moveEventArgs = new MoveEventArgs<IContent>(evtMsgs, moveEventInfo);
if (uow.Events.DispatchCancelable(Trashing, this, moveEventArgs, "Trashing"))
{
uow.Commit();
return OperationStatus.Cancelled(evtMsgs);
}
var moveInfo = new List<MoveEventInfo<IContent>>
{
new MoveEventInfo<IContent>(content, originalPath, Constants.System.RecycleBinContent)
moveEventInfo
};
//get descendents to process of the content item that is being moved to trash - must be done before changing the state below
@@ -1056,7 +1092,9 @@ namespace Umbraco.Core.Services
moveInfo.Add(new MoveEventInfo<IContent>(descendant, descendant.Path, descendant.ParentId));
}
uow.Events.Dispatch(Trashed, this, new MoveEventArgs<IContent>(false, evtMsgs, moveInfo.ToArray()), "Trashed");
moveEventArgs.CanCancel = false;
moveEventArgs.MoveInfoCollection = moveInfo;
uow.Events.Dispatch(Trashed, this, moveEventArgs, "Trashed");
Audit(uow, AuditType.Move, "Move Content to Recycle Bin performed by user", userId, content.Id);
uow.Commit();
@@ -1183,7 +1221,8 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.GetUnitOfWork())
{
if (raiseEvents && uow.Events.DispatchCancelable(Saving, this, new SaveEventArgs<IContent>(asArray, evtMsgs)))
var saveEventArgs = new SaveEventArgs<IContent>(asArray, evtMsgs);
if (raiseEvents && uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
{
uow.Commit();
return OperationStatus.Cancelled(evtMsgs);
@@ -1224,7 +1263,10 @@ namespace Umbraco.Core.Services
}
if (raiseEvents)
uow.Events.Dispatch(Saved, this, new SaveEventArgs<IContent>(asArray, false, evtMsgs));
{
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
}
Audit(uow, AuditType.Save, "Bulk Save content performed by user", userId == -1 ? 0 : userId, Constants.System.Root);
uow.Commit();
@@ -1250,7 +1292,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(Deleting, this, new DeleteEventArgs<IContent>(content, evtMsgs), "Deleting"))
var deleteEventArgs = new DeleteEventArgs<IContent>(content, evtMsgs);
if (uow.Events.DispatchCancelable(Deleting, this, deleteEventArgs, "Deleting"))
{
uow.Commit();
return OperationStatus.Cancelled(evtMsgs);
@@ -1273,8 +1316,8 @@ namespace Umbraco.Core.Services
repository.Delete(content);
var args = new DeleteEventArgs<IContent>(content, false, evtMsgs);
uow.Events.Dispatch(Deleted, this, args, "Deleted");
deleteEventArgs.CanCancel = false;
uow.Events.Dispatch(Deleted, this, deleteEventArgs, "Deleted");
Audit(uow, AuditType.Delete, "Delete Content performed by user", userId, content.Id);
uow.Commit();
@@ -1402,7 +1445,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(DeletingVersions, this, new DeleteRevisionsEventArgs(id, dateToRetain: versionDate), "DeletingVersions"))
var deleteRevisionsEventArgs = new DeleteRevisionsEventArgs(id, dateToRetain: versionDate);
if (uow.Events.DispatchCancelable(DeletingVersions, this, deleteRevisionsEventArgs, "DeletingVersions"))
{
uow.Commit();
return;
@@ -1410,8 +1454,8 @@ namespace Umbraco.Core.Services
var repository = RepositoryFactory.CreateContentRepository(uow);
repository.DeleteVersions(id, versionDate);
uow.Events.Dispatch(DeletedVersions, this, new DeleteRevisionsEventArgs(id, false, dateToRetain: versionDate), "DeletedVersions");
deleteRevisionsEventArgs.CanCancel = false;
uow.Events.Dispatch(DeletedVersions, this, deleteRevisionsEventArgs, "DeletedVersions");
Audit(uow, AuditType.Delete, "Delete Content by version date performed by user", userId, Constants.System.Root);
uow.Commit();
@@ -1490,7 +1534,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(Moving, this, new MoveEventArgs<IContent>(new MoveEventInfo<IContent>(content, content.Path, parentId)), "Moving"))
var moveEventInfo = new MoveEventInfo<IContent>(content, content.Path, parentId);
var moveEventArgs = new MoveEventArgs<IContent>(moveEventInfo);
if (uow.Events.DispatchCancelable(Moving, this, moveEventArgs, "Moving"))
{
uow.Commit();
return;
@@ -1502,7 +1548,9 @@ namespace Umbraco.Core.Services
//call private method that does the recursive moving
PerformMove(content, parentId, userId, moveInfo);
uow.Events.Dispatch(Moved, this, new MoveEventArgs<IContent>(false, moveInfo.ToArray()), "Moved");
moveEventArgs.MoveInfoCollection = moveInfo;
moveEventArgs.CanCancel = false;
uow.Events.Dispatch(Moved, this, moveEventArgs, "Moved");
Audit(uow, AuditType.Move, "Move Content performed by user", userId, content.Id);
uow.Commit();
@@ -1531,15 +1579,17 @@ namespace Umbraco.Core.Services
var files = ((ContentRepository)repository).GetFilesInRecycleBinForUploadField();
if (uow.Events.DispatchCancelable(EmptyingRecycleBin, this, new RecycleBinEventArgs(nodeObjectType, entities, files)))
var recycleBinEventArgs = new RecycleBinEventArgs(nodeObjectType, entities, files);
if (uow.Events.DispatchCancelable(EmptyingRecycleBin, this, recycleBinEventArgs))
{
uow.Commit();
return;
}
var success = repository.EmptyRecycleBin();
uow.Events.Dispatch(EmptiedRecycleBin, this, new RecycleBinEventArgs(nodeObjectType, entities, files, success));
recycleBinEventArgs.CanCancel = false;
recycleBinEventArgs.RecycleBinEmptiedSuccessfully = success;
uow.Events.Dispatch(EmptiedRecycleBin, this, recycleBinEventArgs);
Audit(uow, AuditType.Delete, "Empty Content Recycle Bin performed by user", 0, Constants.System.RecycleBinContent);
uow.Commit();
@@ -1586,7 +1636,8 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(Copying, this, new CopyEventArgs<IContent>(content, copy, parentId)))
var copyEventArgs = new CopyEventArgs<IContent>(content, copy, true, parentId, relateToOriginal);
if (uow.Events.DispatchCancelable(Copying, this, copyEventArgs))
{
uow.Commit();
return null;
@@ -1627,7 +1678,8 @@ namespace Umbraco.Core.Services
Copy(child, copy.Id, relateToOriginal, true, userId);
}
}
uow.Events.Dispatch(Copied, this, new CopyEventArgs<IContent>(content, copy, false, parentId, relateToOriginal));
copyEventArgs.CanCancel = false;
uow.Events.Dispatch(Copied, this, copyEventArgs);
Audit(uow, AuditType.Copy, "Copy Content performed by user", content.WriterId, content.Id);
uow.Commit();
}
@@ -1647,7 +1699,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(SendingToPublish, this, new SendToPublishEventArgs<IContent>(content)))
var sendToPublishEventArgs = new SendToPublishEventArgs<IContent>(content);
if (uow.Events.DispatchCancelable(SendingToPublish, this, sendToPublishEventArgs))
{
uow.Commit();
return false;
@@ -1655,8 +1708,8 @@ namespace Umbraco.Core.Services
//Save before raising event
Save(content, userId);
uow.Events.Dispatch(SentToPublish, this, new SendToPublishEventArgs<IContent>(content, false));
sendToPublishEventArgs.CanCancel = false;
uow.Events.Dispatch(SentToPublish, this, sendToPublishEventArgs);
Audit(uow, AuditType.SendToPublish, "Send to Publish performed by user", content.WriterId, content.Id);
uow.Commit();
@@ -1683,7 +1736,8 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(RollingBack, this, new RollbackEventArgs<IContent>(content)))
var rollbackEventArgs = new RollbackEventArgs<IContent>(content);
if (uow.Events.DispatchCancelable(RollingBack, this, rollbackEventArgs))
{
uow.Commit();
return content;
@@ -1697,8 +1751,8 @@ namespace Umbraco.Core.Services
repository.AddOrUpdate(content);
repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, _userService, c));
uow.Events.Dispatch(RolledBack, this, new RollbackEventArgs<IContent>(content, false));
rollbackEventArgs.CanCancel = false;
uow.Events.Dispatch(RolledBack, this, rollbackEventArgs);
Audit(uow, AuditType.RollBack, "Content rollback performed by user", content.WriterId, content.Id);
uow.Commit();
@@ -1729,7 +1783,8 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.GetUnitOfWork())
{
var asArray = items.ToArray();
if (raiseEvents && uow.Events.DispatchCancelable(Saving, this, new SaveEventArgs<IContent>(asArray)))
var saveEventArgs = new SaveEventArgs<IContent>(asArray);
if (raiseEvents && uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
{
uow.Commit();
return false;
@@ -1774,7 +1829,10 @@ namespace Umbraco.Core.Services
}
if (raiseEvents)
uow.Events.Dispatch(Saved, this, new SaveEventArgs<IContent>(asArray, false));
{
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
}
if (shouldBePublished.Any())
{
@@ -2138,7 +2196,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (raiseEvents && uow.Events.DispatchCancelable(Saving, this, new SaveEventArgs<IContent>(content, evtMsgs)))
var saveEventArgs = new SaveEventArgs<IContent>(content, evtMsgs);
if (raiseEvents && uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
{
uow.Commit();
return Attempt.Fail(new PublishStatus(content, PublishStatusType.FailedCancelledByEvent, evtMsgs));
@@ -2193,7 +2252,10 @@ namespace Umbraco.Core.Services
}
if (raiseEvents)
uow.Events.Dispatch(Saved, this, new SaveEventArgs<IContent>(content, false, evtMsgs));
{
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
}
//Save xml to db and call following method to fire event through PublishingStrategy to update cache
if (published)
@@ -2232,7 +2294,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (raiseEvents && uow.Events.DispatchCancelable(Saving, this, new SaveEventArgs<IContent>(content, evtMsgs)))
var saveEventArgs = new SaveEventArgs<IContent>(content, evtMsgs);
if (raiseEvents && uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
{
uow.Commit();
return OperationStatus.Cancelled(evtMsgs);
@@ -2261,7 +2324,10 @@ namespace Umbraco.Core.Services
repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, _userService, c));
if (raiseEvents)
uow.Events.Dispatch(Saved, this, new SaveEventArgs<IContent>(content, false, evtMsgs));
{
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
}
Audit(uow, AuditType.Save, "Save Content performed by user", userId, content.Id);
uow.Commit();
@@ -2271,40 +2337,6 @@ namespace Umbraco.Core.Services
}
}
/// <summary>
/// Checks if the passed in <see cref="IContent"/> can be published based on the anscestors publish state.
/// </summary>
/// <remarks>
/// Check current is only used when falling back to checking the Parent of non-saved content, as
/// non-saved content doesn't have a valid path yet.
/// </remarks>
/// <param name="content"><see cref="IContent"/> to check if anscestors are published</param>
/// <param name="checkCurrent">Boolean indicating whether the passed in content should also be checked for published versions</param>
/// <returns>True if the Content can be published, otherwise False</returns>
private bool IsPublishable(IContent content, bool checkCurrent)
{
var ids = content.Path.Split(',').Select(int.Parse).ToList();
foreach (var id in ids)
{
//If Id equals that of the recycle bin we return false because nothing in the bin can be published
if (id == Constants.System.RecycleBinContent)
return false;
//We don't check the System Root, so just continue
if (id == Constants.System.Root) continue;
//If the current id equals that of the passed in content and if current shouldn't be checked we skip it.
if (checkCurrent == false && id == content.Id) continue;
//Check if the content for the current id is published - escape the loop if we encounter content that isn't published
var hasPublishedVersion = HasPublishedVersion(id);
if (hasPublishedVersion == false)
return false;
}
return true;
}
private PublishStatusType CheckAndLogIsPublishable(IContent content)
{
//Check if parent is published (although not if its a root node) - if parent isn't published this Content cannot be published
+64 -36
View File
@@ -54,7 +54,8 @@ namespace Umbraco.Core.Services
CreatorId = userId
};
if (uow.Events.DispatchCancelable(SavingContentTypeContainer, this, new SaveEventArgs<EntityContainer>(container, evtMsgs)))
var saveEventArgs = new SaveEventArgs<EntityContainer>(container, evtMsgs);
if (uow.Events.DispatchCancelable(SavingContentTypeContainer, this, saveEventArgs))
{
uow.Commit();
return Attempt.Fail(new OperationStatus<EntityContainer, OperationStatusType>(container, OperationStatusType.FailedCancelledByEvent, evtMsgs));
@@ -62,8 +63,8 @@ namespace Umbraco.Core.Services
repo.AddOrUpdate(container);
uow.Commit();
uow.Events.Dispatch(SavedContentTypeContainer, this, new SaveEventArgs<EntityContainer>(container, evtMsgs), "SavedContentTypeContainer");
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(SavedContentTypeContainer, this, saveEventArgs, "SavedContentTypeContainer");
//TODO: Audit trail ?
return Attempt.Succeed(new OperationStatus<EntityContainer, OperationStatusType>(container, OperationStatusType.Success, evtMsgs));
@@ -92,7 +93,8 @@ namespace Umbraco.Core.Services
CreatorId = userId
};
if (uow.Events.DispatchCancelable(SavingMediaTypeContainer, this, new SaveEventArgs<EntityContainer>(container, evtMsgs)))
var saveEventArgs = new SaveEventArgs<EntityContainer>(container, evtMsgs);
if (uow.Events.DispatchCancelable(SavingMediaTypeContainer, this, saveEventArgs))
{
uow.Commit();
return Attempt.Fail(new OperationStatus<EntityContainer, OperationStatusType>(container, OperationStatusType.FailedCancelledByEvent, evtMsgs));
@@ -100,8 +102,8 @@ namespace Umbraco.Core.Services
repo.AddOrUpdate(container);
uow.Commit();
uow.Events.Dispatch(SavedMediaTypeContainer, this, new SaveEventArgs<EntityContainer>(container, evtMsgs), "SavedMediaTypeContainer");
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(SavedMediaTypeContainer, this, saveEventArgs, "SavedMediaTypeContainer");
//TODO: Audit trail ?
return Attempt.Succeed(new OperationStatus<EntityContainer, OperationStatusType>(container, OperationStatusType.Success, evtMsgs));
@@ -289,7 +291,8 @@ namespace Umbraco.Core.Services
return OperationStatus.NoOperation(evtMsgs);
}
if (uow.Events.DispatchCancelable(DeletingContentTypeContainer, this, new DeleteEventArgs<EntityContainer>(container, evtMsgs)))
var deleteEventArgs = new DeleteEventArgs<EntityContainer>(container, evtMsgs);
if (uow.Events.DispatchCancelable(DeletingContentTypeContainer, this, deleteEventArgs))
{
uow.Commit();
return Attempt.Fail(new OperationStatus(OperationStatusType.FailedCancelledByEvent, evtMsgs));
@@ -297,8 +300,8 @@ namespace Umbraco.Core.Services
repo.Delete(container);
uow.Commit();
uow.Events.Dispatch(DeletedContentTypeContainer, this, new DeleteEventArgs<EntityContainer>(container, evtMsgs), "DeletedContentTypeContainer");
deleteEventArgs.CanCancel = false;
uow.Events.Dispatch(DeletedContentTypeContainer, this, deleteEventArgs, "DeletedContentTypeContainer");
return OperationStatus.Success(evtMsgs);
//TODO: Audit trail ?
@@ -319,7 +322,8 @@ namespace Umbraco.Core.Services
return OperationStatus.NoOperation(evtMsgs);
}
if (uow.Events.DispatchCancelable(DeletingMediaTypeContainer, this, new DeleteEventArgs<EntityContainer>(container, evtMsgs)))
var deleteEventArgs = new DeleteEventArgs<EntityContainer>(container, evtMsgs);
if (uow.Events.DispatchCancelable(DeletingMediaTypeContainer, this, deleteEventArgs))
{
uow.Commit();
return Attempt.Fail(new OperationStatus(OperationStatusType.FailedCancelledByEvent, evtMsgs));
@@ -327,8 +331,8 @@ namespace Umbraco.Core.Services
repo.Delete(container);
uow.Commit();
uow.Events.Dispatch(DeletedMediaTypeContainer, this, new DeleteEventArgs<EntityContainer>(container, evtMsgs));
deleteEventArgs.CanCancel = false;
uow.Events.Dispatch(DeletedMediaTypeContainer, this, deleteEventArgs);
return OperationStatus.Success(evtMsgs);
//TODO: Audit trail ?
@@ -756,7 +760,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(SavingContentType, this, new SaveEventArgs<IContentType>(contentType)))
var saveEventArgs = new SaveEventArgs<IContentType>(contentType);
if (uow.Events.DispatchCancelable(SavingContentType, this, saveEventArgs))
{
uow.Commit();
return;
@@ -779,7 +784,8 @@ namespace Umbraco.Core.Services
UpdateContentXmlStructure(contentType);
uow.Events.Dispatch(SavedContentType, this, new SaveEventArgs<IContentType>(contentType, false));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(SavedContentType, this, saveEventArgs);
Audit(uow, AuditType.Save, "Save ContentType performed by user", userId, contentType.Id);
uow.Commit();
@@ -800,7 +806,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(SavingContentType, this, new SaveEventArgs<IContentType>(asArray)))
var saveEventArgs = new SaveEventArgs<IContentType>(asArray);
if (uow.Events.DispatchCancelable(SavingContentType, this, saveEventArgs))
{
uow.Commit();
return;
@@ -825,7 +832,8 @@ namespace Umbraco.Core.Services
UpdateContentXmlStructure(asArray.Cast<IContentTypeBase>().ToArray());
uow.Events.Dispatch(SavedContentType, this, new SaveEventArgs<IContentType>(asArray, false));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(SavedContentType, this, saveEventArgs);
Audit(uow, AuditType.Save, "Save ContentTypes performed by user", userId, -1);
uow.Commit();
@@ -845,7 +853,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(DeletingContentType, this, new DeleteEventArgs<IContentType>(contentType)))
var deleteEventArgs = new DeleteEventArgs<IContentType>(contentType);
if (uow.Events.DispatchCancelable(DeletingContentType, this, deleteEventArgs))
{
uow.Commit();
return;
@@ -860,8 +869,9 @@ namespace Umbraco.Core.Services
_contentService.DeleteContentOfTypes(deletedContentTypes.Select(x => x.Id), userId);
repository.Delete(contentType);
uow.Events.Dispatch(DeletedContentType, this, new DeleteEventArgs<IContentType>(deletedContentTypes.DistinctBy(x => x.Id), false));
deleteEventArgs.DeletedEntities = deletedContentTypes.DistinctBy(x => x.Id);
deleteEventArgs.CanCancel = false;
uow.Events.Dispatch(DeletedContentType, this, deleteEventArgs);
Audit(uow, AuditType.Delete, string.Format("Delete ContentType performed by user"), userId, contentType.Id);
uow.Commit();
@@ -885,7 +895,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(DeletingContentType, this, new DeleteEventArgs<IContentType>(asArray)))
var deleteEventArgs = new DeleteEventArgs<IContentType>(asArray);
if (uow.Events.DispatchCancelable(DeletingContentType, this, deleteEventArgs))
{
uow.Commit();
return;
@@ -906,8 +917,9 @@ namespace Umbraco.Core.Services
{
repository.Delete(contentType);
}
uow.Events.Dispatch(DeletedContentType, this, new DeleteEventArgs<IContentType>(deletedContentTypes.DistinctBy(x => x.Id), false));
deleteEventArgs.DeletedEntities = deletedContentTypes.DistinctBy(x => x.Id);
deleteEventArgs.CanCancel = false;
uow.Events.Dispatch(DeletedContentType, this, deleteEventArgs);
Audit(uow, AuditType.Delete, string.Format("Delete ContentTypes performed by user"), userId, -1);
uow.Commit();
@@ -1056,7 +1068,9 @@ namespace Umbraco.Core.Services
var moveInfo = new List<MoveEventInfo<IMediaType>>();
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(MovingMediaType, this, new MoveEventArgs<IMediaType>(evtMsgs, new MoveEventInfo<IMediaType>(toMove, toMove.Path, containerId))))
var moveEventInfo = new MoveEventInfo<IMediaType>(toMove, toMove.Path, containerId);
var moveEventArgs = new MoveEventArgs<IMediaType>(evtMsgs, moveEventInfo);
if (uow.Events.DispatchCancelable(MovingMediaType, this, moveEventArgs))
{
uow.Commit();
return Attempt.Fail(new OperationStatus<MoveOperationStatusType>(MoveOperationStatusType.FailedCancelledByEvent, evtMsgs));
@@ -1081,7 +1095,9 @@ namespace Umbraco.Core.Services
return Attempt.Fail(new OperationStatus<MoveOperationStatusType>(ex.Operation, evtMsgs));
}
uow.Commit();
uow.Events.Dispatch(MovedMediaType, this, new MoveEventArgs<IMediaType>(false, evtMsgs, moveInfo.ToArray()));
moveEventArgs.MoveInfoCollection = moveInfo;
moveEventArgs.CanCancel = false;
uow.Events.Dispatch(MovedMediaType, this, moveEventArgs);
}
return Attempt.Succeed(
@@ -1095,7 +1111,9 @@ namespace Umbraco.Core.Services
var moveInfo = new List<MoveEventInfo<IContentType>>();
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(MovingContentType, this, new MoveEventArgs<IContentType>(evtMsgs, new MoveEventInfo<IContentType>(toMove, toMove.Path, containerId))))
var moveEventInfo = new MoveEventInfo<IContentType>(toMove, toMove.Path, containerId);
var moveEventArgs = new MoveEventArgs<IContentType>(evtMsgs, moveEventInfo);
if (uow.Events.DispatchCancelable(MovingContentType, this, moveEventArgs))
{
uow.Commit();
return Attempt.Fail(new OperationStatus<MoveOperationStatusType>(MoveOperationStatusType.FailedCancelledByEvent, evtMsgs));
@@ -1119,8 +1137,10 @@ namespace Umbraco.Core.Services
uow.Commit();
return Attempt.Fail(new OperationStatus<MoveOperationStatusType>(ex.Operation, evtMsgs));
}
moveEventArgs.MoveInfoCollection = moveInfo;
moveEventArgs.CanCancel = false;
uow.Commit();
uow.Events.Dispatch(MovedContentType, this, new MoveEventArgs<IContentType>(false, evtMsgs, moveInfo.ToArray()));
uow.Events.Dispatch(MovedContentType, this, moveEventArgs);
}
return Attempt.Succeed(
@@ -1228,7 +1248,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(SavingMediaType, this, new SaveEventArgs<IMediaType>(mediaType)))
var saveEventArgs = new SaveEventArgs<IMediaType>(mediaType);
if (uow.Events.DispatchCancelable(SavingMediaType, this, saveEventArgs))
{
uow.Commit();
return;
@@ -1244,8 +1265,8 @@ namespace Umbraco.Core.Services
uow.Commit();
UpdateContentXmlStructure(mediaType);
uow.Events.Dispatch(SavedMediaType, this, new SaveEventArgs<IMediaType>(mediaType, false));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(SavedMediaType, this, saveEventArgs);
Audit(uow, AuditType.Save, "Save MediaType performed by user", userId, mediaType.Id);
uow.Commit();
@@ -1266,7 +1287,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(SavingMediaType, this, new SaveEventArgs<IMediaType>(asArray)))
var saveEventArgs = new SaveEventArgs<IMediaType>(asArray);
if (uow.Events.DispatchCancelable(SavingMediaType, this, saveEventArgs))
{
uow.Commit();
return;
@@ -1290,8 +1312,8 @@ namespace Umbraco.Core.Services
uow.Commit();
UpdateContentXmlStructure(asArray.Cast<IContentTypeBase>().ToArray());
uow.Events.Dispatch(SavedMediaType, this, new SaveEventArgs<IMediaType>(asArray, false));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(SavedMediaType, this, saveEventArgs);
Audit(uow, AuditType.Save, "Save MediaTypes performed by user", userId, -1);
uow.Commit();
@@ -1313,7 +1335,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(DeletingMediaType, this, new DeleteEventArgs<IMediaType>(mediaType)))
var deleteEventArgs = new DeleteEventArgs<IMediaType>(mediaType);
if (uow.Events.DispatchCancelable(DeletingMediaType, this, deleteEventArgs))
{
uow.Commit();
return;
@@ -1329,7 +1352,9 @@ namespace Umbraco.Core.Services
repository.Delete(mediaType);
uow.Events.Dispatch(DeletedMediaType, this, new DeleteEventArgs<IMediaType>(deletedMediaTypes.DistinctBy(x => x.Id), false));
deleteEventArgs.CanCancel = false;
deleteEventArgs.DeletedEntities = deletedMediaTypes.DistinctBy(x => x.Id);
uow.Events.Dispatch(DeletedMediaType, this, deleteEventArgs);
Audit(uow, AuditType.Delete, "Delete MediaType performed by user", userId, mediaType.Id);
uow.Commit();
@@ -1353,7 +1378,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(DeletingMediaType, this, new DeleteEventArgs<IMediaType>(asArray)))
var deleteEventArgs = new DeleteEventArgs<IMediaType>(asArray);
if (uow.Events.DispatchCancelable(DeletingMediaType, this, deleteEventArgs))
{
uow.Commit();
return;
@@ -1375,7 +1401,9 @@ namespace Umbraco.Core.Services
repository.Delete(mediaType);
}
uow.Events.Dispatch(DeletedMediaType, this, new DeleteEventArgs<IMediaType>(deletedMediaTypes.DistinctBy(x => x.Id), false));
deleteEventArgs.DeletedEntities = deletedMediaTypes.DistinctBy(x => x.Id);
deleteEventArgs.CanCancel = false;
uow.Events.Dispatch(DeletedMediaType, this, deleteEventArgs);
Audit(uow, AuditType.Delete, "Delete MediaTypes performed by user", userId, -1);
uow.Commit();
+25 -14
View File
@@ -312,7 +312,9 @@ namespace Umbraco.Core.Services
var moveInfo = new List<MoveEventInfo<IDataTypeDefinition>>();
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(Moving, this, new MoveEventArgs<IDataTypeDefinition>(evtMsgs, new MoveEventInfo<IDataTypeDefinition>(toMove, toMove.Path, parentId))))
var moveEventInfo = new MoveEventInfo<IDataTypeDefinition>(toMove, toMove.Path, parentId);
var moveEventArgs = new MoveEventArgs<IDataTypeDefinition>(evtMsgs, moveEventInfo);
if (uow.Events.DispatchCancelable(Moving, this, moveEventArgs))
{
uow.Commit();
return Attempt.Fail(new OperationStatus<MoveOperationStatusType>(MoveOperationStatusType.FailedCancelledByEvent, evtMsgs));
@@ -338,7 +340,9 @@ namespace Umbraco.Core.Services
new OperationStatus<MoveOperationStatusType>(ex.Operation, evtMsgs));
}
uow.Commit();
uow.Events.Dispatch(Moved, this, new MoveEventArgs<IDataTypeDefinition>(false, evtMsgs, moveInfo.ToArray()));
moveEventArgs.MoveInfoCollection = moveInfo;
moveEventArgs.CanCancel = false;
uow.Events.Dispatch(Moved, this, moveEventArgs);
}
return Attempt.Succeed(
@@ -354,7 +358,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(Saving, this, new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinition)))
var saveEventArgs = new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinition);
if (uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
{
uow.Commit();
return;
@@ -369,8 +374,8 @@ namespace Umbraco.Core.Services
dataTypeDefinition.CreatorId = userId;
repository.AddOrUpdate(dataTypeDefinition);
uow.Events.Dispatch(Saved, this, new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinition, false));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
Audit(uow, AuditType.Save, "Save DataTypeDefinition performed by user", userId, dataTypeDefinition.Id);
uow.Commit();
@@ -397,9 +402,10 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
var saveEventArgs = new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinitions);
if (raiseEvents)
{
if (uow.Events.DispatchCancelable(Saving, this, new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinitions)))
{
if (uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
{
uow.Commit();
return;
@@ -415,7 +421,10 @@ namespace Umbraco.Core.Services
}
if (raiseEvents)
uow.Events.Dispatch(Saved, this, new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinitions, false));
{
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
}
Audit(uow, AuditType.Save, string.Format("Save DataTypeDefinition performed by user"), userId, -1);
uow.Commit();
@@ -504,7 +513,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(Saving, this, new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinition)))
var saveEventArgs = new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinition);
if (uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
{
uow.Commit();
return;
@@ -526,8 +536,8 @@ namespace Umbraco.Core.Services
Audit(uow, AuditType.Save, string.Format("Save DataTypeDefinition performed by user"), userId, dataTypeDefinition.Id);
uow.Commit();
uow.Events.Dispatch(Saved, this, new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinition, false));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
}
}
@@ -544,7 +554,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(Deleting, this, new DeleteEventArgs<IDataTypeDefinition>(dataTypeDefinition)))
var deleteEventArgs = new DeleteEventArgs<IDataTypeDefinition>(dataTypeDefinition);
if (uow.Events.DispatchCancelable(Deleting, this, deleteEventArgs))
{
uow.Commit();
return;
@@ -556,8 +567,8 @@ namespace Umbraco.Core.Services
Audit(uow, AuditType.Delete, "Delete DataTypeDefinition performed by user", userId, dataTypeDefinition.Id);
uow.Commit();
uow.Events.Dispatch(Deleted, this, new DeleteEventArgs<IDataTypeDefinition>(dataTypeDefinition, false));
deleteEventArgs.CanCancel = false;
uow.Events.Dispatch(Deleted, this, deleteEventArgs);
}
}
+8 -5
View File
@@ -32,7 +32,8 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(Deleting, this, new DeleteEventArgs<IDomain>(domain, evtMsgs)))
var deleteEventArgs = new DeleteEventArgs<IDomain>(domain, evtMsgs);
if (uow.Events.DispatchCancelable(Deleting, this, deleteEventArgs))
{
uow.Commit();
return OperationStatus.Cancelled(evtMsgs);
@@ -42,8 +43,8 @@ namespace Umbraco.Core.Services
repository.Delete(domain);
uow.Commit();
var args = new DeleteEventArgs<IDomain>(domain, false, evtMsgs);
uow.Events.Dispatch(Deleted, this, args);
deleteEventArgs.CanCancel = false;
uow.Events.Dispatch(Deleted, this, deleteEventArgs);
return OperationStatus.Success(evtMsgs);
}
@@ -92,7 +93,8 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(Saving, this, new SaveEventArgs<IDomain>(domainEntity, evtMsgs)))
var saveEventArgs = new SaveEventArgs<IDomain>(domainEntity, evtMsgs);
if (uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
{
uow.Commit();
return OperationStatus.Cancelled(evtMsgs);
@@ -101,7 +103,8 @@ namespace Umbraco.Core.Services
var repository = RepositoryFactory.CreateDomainRepository(uow);
repository.AddOrUpdate(domainEntity);
uow.Commit();
uow.Events.Dispatch(Saved, this, new SaveEventArgs<IDomain>(domainEntity, false, evtMsgs));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
return OperationStatus.Success(evtMsgs);
}
@@ -646,5 +646,34 @@ namespace Umbraco.Core.Services
return exists;
}
}
/// <inheritdoc />
public int ReserveId(Guid key)
{
NodeDto node;
using (var scope = UowProvider.ScopeProvider.CreateScope())
{
var sql = new Sql("SELECT * FROM umbracoNode WHERE uniqueID=@0 AND nodeObjectType=@1", key, Constants.ObjectTypes.IdReservationGuid);
node = scope.Database.SingleOrDefault<NodeDto>(sql);
if (node != null) throw new InvalidOperationException("An identifier has already been reserved for this Udi.");
node = new NodeDto
{
UniqueId = key,
Text = "RESERVED.ID",
NodeObjectType = Constants.ObjectTypes.IdReservationGuid,
CreateDate = DateTime.Now,
UserId = 0,
ParentId = -1,
Level = 1,
Path = "-1",
SortOrder = 0,
Trashed = false
};
scope.Database.Insert(node);
scope.Complete();
}
return node.NodeId;
}
}
}
+42 -31
View File
@@ -70,7 +70,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(SavingStylesheet, this, new SaveEventArgs<Stylesheet>(stylesheet)))
var saveEventArgs = new SaveEventArgs<Stylesheet>(stylesheet);
if (uow.Events.DispatchCancelable(SavingStylesheet, this, saveEventArgs))
{
uow.Commit();
return;
@@ -78,8 +79,8 @@ namespace Umbraco.Core.Services
var repository = RepositoryFactory.CreateStylesheetRepository(uow);
repository.AddOrUpdate(stylesheet);
uow.Events.Dispatch(SavedStylesheet, this, new SaveEventArgs<Stylesheet>(stylesheet, false));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(SavedStylesheet, this, saveEventArgs);
Audit(uow, AuditType.Save, "Save Stylesheet performed by user", userId, -1);
uow.Commit();
@@ -103,15 +104,16 @@ namespace Umbraco.Core.Services
return;
}
if (uow.Events.DispatchCancelable(DeletingStylesheet, this, new DeleteEventArgs<Stylesheet>(stylesheet)))
var deleteEventArgs = new DeleteEventArgs<Stylesheet>(stylesheet);
if (uow.Events.DispatchCancelable(DeletingStylesheet, this, deleteEventArgs))
{
uow.Commit();
return;
}
repository.Delete(stylesheet);
uow.Events.Dispatch(DeletedStylesheet, this, new DeleteEventArgs<Stylesheet>(stylesheet, false));
deleteEventArgs.CanCancel = false;
uow.Events.Dispatch(DeletedStylesheet, this, deleteEventArgs);
Audit(uow, AuditType.Delete, string.Format("Delete Stylesheet performed by user"), userId, -1);
uow.Commit();
@@ -171,7 +173,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(SavingScript, this, new SaveEventArgs<Script>(script)))
var saveEventArgs = new SaveEventArgs<Script>(script);
if (uow.Events.DispatchCancelable(SavingScript, this, saveEventArgs))
{
uow.Commit();
return;
@@ -179,8 +182,8 @@ namespace Umbraco.Core.Services
var repository = RepositoryFactory.CreateScriptRepository(uow);
repository.AddOrUpdate(script);
uow.Events.Dispatch(SavedScript, this, new SaveEventArgs<Script>(script, false));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(SavedScript, this, saveEventArgs);
Audit(uow, AuditType.Save, "Save Script performed by user", userId, -1);
uow.Commit();
@@ -204,15 +207,16 @@ namespace Umbraco.Core.Services
return;
}
if (uow.Events.DispatchCancelable(DeletingScript, this, new DeleteEventArgs<Script>(script)))
var deleteEventArgs = new DeleteEventArgs<Script>(script);
if (uow.Events.DispatchCancelable(DeletingScript, this, deleteEventArgs))
{
uow.Commit();
return;
}
repository.Delete(script);
uow.Events.Dispatch(DeletedScript, this, new DeleteEventArgs<Script>(script, false));
deleteEventArgs.CanCancel = false;
uow.Events.Dispatch(DeletedScript, this, deleteEventArgs);
Audit(uow, AuditType.Delete, string.Format("Delete Script performed by user"), userId, -1);
uow.Commit();
@@ -289,7 +293,8 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(SavingTemplate, this, new SaveEventArgs<ITemplate>(template, true, evtMsgs, additionalData)))
var saveEventArgs = new SaveEventArgs<ITemplate>(template, true, evtMsgs, additionalData);
if (uow.Events.DispatchCancelable(SavingTemplate, this, saveEventArgs))
{
uow.Commit();
return Attempt.Fail(new OperationStatus<ITemplate, OperationStatusType>(template, OperationStatusType.FailedCancelledByEvent, evtMsgs));
@@ -297,8 +302,8 @@ namespace Umbraco.Core.Services
var repository = RepositoryFactory.CreateTemplateRepository(uow);
repository.AddOrUpdate(template);
uow.Events.Dispatch(SavedTemplate, this, new SaveEventArgs<ITemplate>(template, false, evtMsgs));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(SavedTemplate, this, saveEventArgs);
Audit(uow, AuditType.Save, "Save Template performed by user", userId, template.Id);
uow.Commit();
@@ -562,18 +567,21 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateTemplateRepository(uow);
if (uow.Events.DispatchCancelable(DeletingTemplate, this, new DeleteEventArgs<ITemplate>(template)))
var repository = RepositoryFactory.CreateTemplateRepository(uow);
var args = new DeleteEventArgs<ITemplate>(template);
if (uow.Events.DispatchCancelable(DeletingTemplate, this, args))
{
uow.Commit();
return;
}
repository.Delete(template);
uow.Events.Dispatch(DeletedTemplate, this, new DeleteEventArgs<ITemplate>(template, false));
args.CanCancel = false;
uow.Events.Dispatch(DeletedTemplate, this, args);
Audit(uow, AuditType.Delete, "Delete Template performed by user", userId, template.Id);
uow.Commit();
}
@@ -898,10 +906,11 @@ namespace Umbraco.Core.Services
private Attempt<IPartialView> CreatePartialViewMacro(IPartialView partialView, PartialViewType partialViewType, string snippetName = null, int userId = 0)
{
var newEventArgs = new NewEventArgs<IPartialView>(partialView, true, partialView.Alias, -1);
using (var scope = UowProvider.ScopeProvider.CreateScope())
{
scope.Complete(); // always
if (scope.Events.DispatchCancelable(CreatingPartialView, this, new NewEventArgs<IPartialView>(partialView, true, partialView.Alias, -1)))
scope.Complete(); // always
if (scope.Events.DispatchCancelable(CreatingPartialView, this, newEventArgs))
return Attempt<IPartialView>.Fail();
}
@@ -915,8 +924,8 @@ namespace Umbraco.Core.Services
{
var repository = GetPartialViewRepository(partialViewType, uow);
repository.AddOrUpdate(partialView);
uow.Events.Dispatch(CreatedPartialView, this, new NewEventArgs<IPartialView>(partialView, false, partialView.Alias, -1));
newEventArgs.CanCancel = false;
uow.Events.Dispatch(CreatedPartialView, this, newEventArgs);
Audit(uow, AuditType.Save, string.Format("Save {0} performed by user", partialViewType), userId, -1);
uow.Commit();
@@ -947,15 +956,16 @@ namespace Umbraco.Core.Services
return false;
}
if (uow.Events.DispatchCancelable(DeletingPartialView, this, new DeleteEventArgs<IPartialView>(partialView)))
var deleteEventArgs = new DeleteEventArgs<IPartialView>(partialView);
if (uow.Events.DispatchCancelable(DeletingPartialView, this, deleteEventArgs))
{
uow.Commit();
return false;
}
repository.Delete(partialView);
uow.Events.Dispatch(DeletedPartialView, this, new DeleteEventArgs<IPartialView>(partialView, false));
deleteEventArgs.CanCancel = false;
uow.Events.Dispatch(DeletedPartialView, this, deleteEventArgs);
Audit(uow, AuditType.Delete, string.Format("Delete {0} performed by user", partialViewType), userId, -1);
uow.Commit();
@@ -979,7 +989,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(SavingPartialView, this, new SaveEventArgs<IPartialView>(partialView)))
var saveEventArgs = new SaveEventArgs<IPartialView>(partialView);
if (uow.Events.DispatchCancelable(SavingPartialView, this, saveEventArgs))
{
uow.Commit();
return Attempt<IPartialView>.Fail();
@@ -987,8 +998,8 @@ namespace Umbraco.Core.Services
var repository = GetPartialViewRepository(partialViewType, uow);
repository.AddOrUpdate(partialView);
uow.Events.Dispatch(SavedPartialView, this, new SaveEventArgs<IPartialView>(partialView, false));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(SavedPartialView, this, saveEventArgs);
Audit(uow, AuditType.Save, string.Format("Save {0} performed by user", partialViewType), userId, -1);
uow.Commit();
+9 -1
View File
@@ -170,7 +170,7 @@ namespace Umbraco.Core.Services
/// <param name="totalRecords"></param>
/// <param name="orderBy"></param>
/// <param name="orderDirection"></param>
/// <param name="filter"></param>
/// <param name="filter"></param>
/// <returns></returns>
IEnumerable<IUmbracoEntity> GetPagedDescendants(int id, UmbracoObjectTypes umbracoObjectType, long pageIndex, int pageSize, out long totalRecords,
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "");
@@ -270,5 +270,13 @@ namespace Umbraco.Core.Services
/// <param name="umbracoObjectType"><see cref="UmbracoObjectTypes"/></param>
/// <returns>Type of the entity</returns>
Type GetEntityType(UmbracoObjectTypes umbracoObjectType);
/// <summary>
/// Reserves an identifier for a key.
/// </summary>
/// <param name="key">They key.</param>
/// <returns>The identifier.</returns>
/// <remarks>When a new content or a media is saved with the key, it will have the reserved identifier.</remarks>
int ReserveId(Guid key);
}
}
@@ -136,5 +136,11 @@ namespace Umbraco.Core.Services
/// <param name="language"><see cref="ILanguage"/> to delete</param>
/// <param name="userId">Optional id of the user deleting the language</param>
void Delete(ILanguage language, int userId = 0);
/// <summary>
/// Gets the full dictionary key map.
/// </summary>
/// <returns>The full dictionary key map.</returns>
Dictionary<string, Guid> GetDictionaryItemKeyMap();
}
}
+9 -3
View File
@@ -119,10 +119,16 @@ namespace Umbraco.Core.Services
IMember CreateMemberWithIdentity(string username, string email, string name, IMemberType memberType);
/// <summary>
/// This is simply a helper method which essentially just wraps the MembershipProvider's ChangePassword method
/// This is simply a helper method which essentially just wraps the MembershipProvider's ChangePassword method which can be
/// used during Member creation.
/// </summary>
/// <remarks>This method exists so that Umbraco developers can use one entry point to create/update
/// Members if they choose to. </remarks>
/// <remarks>
/// This method exists so that Umbraco developers can use this entry point to set a password when Creating members ...
/// this will not work for updating members in most cases (depends on your membership provider settings)
///
/// It is preferred to use the membership APIs for working with passwords, in the near future this method will be obsoleted
/// and the ASP.NET Identity APIs should be used instead.
/// </remarks>
/// <param name="member">The Member to save the password for</param>
/// <param name="password">The password to encrypt and save</param>
void SavePassword(IMember member, string password);
+47 -18
View File
@@ -10,8 +10,9 @@ namespace Umbraco.Core.Services
{
private readonly IDatabaseUnitOfWorkProvider _uowProvider;
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim();
private readonly Dictionary<int, Guid> _id2Key = new Dictionary<int, Guid>();
private readonly Dictionary<Guid, int> _key2Id = new Dictionary<Guid, int>();
private readonly Dictionary<int, TypedId<Guid>> _id2Key = new Dictionary<int, TypedId<Guid>>();
private readonly Dictionary<Guid, TypedId<int>> _key2Id = new Dictionary<Guid, TypedId<int>>();
public IdkMap(IDatabaseUnitOfWorkProvider uowProvider)
{
@@ -23,11 +24,11 @@ namespace Umbraco.Core.Services
public Attempt<int> GetIdForKey(Guid key, UmbracoObjectTypes umbracoObjectType)
{
int id;
TypedId<int> id;
try
{
_locker.EnterReadLock();
if (_key2Id.TryGetValue(key, out id)) return Attempt.Succeed(id);
if (_key2Id.TryGetValue(key, out id) && id.UmbracoObjectType == umbracoObjectType) return Attempt.Succeed(id.Id);
}
finally
{
@@ -44,13 +45,16 @@ namespace Umbraco.Core.Services
}
if (val == null) return Attempt<int>.Fail();
id = val.Value;
// cache reservations, when something is saved this cache is cleared anyways
//if (umbracoObjectType == UmbracoObjectTypes.IdReservation)
// Attempt.Succeed(val.Value);
try
{
_locker.EnterWriteLock();
_id2Key[id] = key;
_key2Id[key] = id;
_id2Key[val.Value] = new TypedId<Guid>(key, umbracoObjectType);
_key2Id[key] = new TypedId<int>(val.Value, umbracoObjectType);
}
finally
{
@@ -58,7 +62,7 @@ namespace Umbraco.Core.Services
_locker.ExitWriteLock();
}
return Attempt.Succeed(id);
return Attempt.Succeed(val.Value);
}
public Attempt<int> GetIdForUdi(Udi udi)
@@ -73,11 +77,11 @@ namespace Umbraco.Core.Services
public Attempt<Guid> GetKeyForId(int id, UmbracoObjectTypes umbracoObjectType)
{
Guid key;
TypedId<Guid> key;
try
{
_locker.EnterReadLock();
if (_id2Key.TryGetValue(id, out key)) return Attempt.Succeed(key);
if (_id2Key.TryGetValue(id, out key) && key.UmbracoObjectType == umbracoObjectType) return Attempt.Succeed(key.Id);
}
finally
{
@@ -94,13 +98,16 @@ namespace Umbraco.Core.Services
}
if (val == null) return Attempt<Guid>.Fail();
key = val.Value;
// cache reservations, when something is saved this cache is cleared anyways
//if (umbracoObjectType == UmbracoObjectTypes.IdReservation)
// Attempt.Succeed(val.Value);
try
{
_locker.EnterWriteLock();
_id2Key[id] = key;
_key2Id[key] = id;
_id2Key[id] = new TypedId<Guid>(val.Value, umbracoObjectType); ;
_key2Id[val.Value] = new TypedId<int>();
}
finally
{
@@ -108,7 +115,7 @@ namespace Umbraco.Core.Services
_locker.ExitWriteLock();
}
return Attempt.Succeed(key);
return Attempt.Succeed(val.Value);
}
private static Guid GetNodeObjectTypeGuid(UmbracoObjectTypes umbracoObjectType)
@@ -139,10 +146,10 @@ namespace Umbraco.Core.Services
try
{
_locker.EnterWriteLock();
Guid key;
TypedId<Guid> key;
if (_id2Key.TryGetValue(id, out key) == false) return;
_id2Key.Remove(id);
_key2Id.Remove(key);
_key2Id.Remove(key.Id);
}
finally
{
@@ -156,9 +163,9 @@ namespace Umbraco.Core.Services
try
{
_locker.EnterWriteLock();
int id;
TypedId<int> id;
if (_key2Id.TryGetValue(key, out id) == false) return;
_id2Key.Remove(id);
_id2Key.Remove(id.Id);
_key2Id.Remove(key);
}
finally
@@ -167,5 +174,27 @@ namespace Umbraco.Core.Services
_locker.ExitWriteLock();
}
}
private struct TypedId<T>
{
private readonly T _id;
private readonly UmbracoObjectTypes _umbracoObjectType;
public T Id
{
get { return _id; }
}
public UmbracoObjectTypes UmbracoObjectType
{
get { return _umbracoObjectType; }
}
public TypedId(T id, UmbracoObjectTypes umbracoObjectType)
{
_umbracoObjectType = umbracoObjectType;
_id = id;
}
}
}
}
@@ -79,7 +79,8 @@ namespace Umbraco.Core.Services
item.Translations = translations;
}
if (uow.Events.DispatchCancelable(SavingDictionaryItem, this, new SaveEventArgs<IDictionaryItem>(item)))
var saveEventArgs = new SaveEventArgs<IDictionaryItem>(item);
if (uow.Events.DispatchCancelable(SavingDictionaryItem, this, saveEventArgs))
{
uow.Commit();
return item;
@@ -90,8 +91,8 @@ namespace Umbraco.Core.Services
//ensure the lazy Language callback is assigned
EnsureDictionaryItemLanguageCallback(item);
uow.Events.Dispatch(SavedDictionaryItem, this, new SaveEventArgs<IDictionaryItem>(item));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(SavedDictionaryItem, this, saveEventArgs);
return item;
}
@@ -256,7 +257,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(DeletingDictionaryItem, this, new DeleteEventArgs<IDictionaryItem>(dictionaryItem)))
var deleteEventArgs = new DeleteEventArgs<IDictionaryItem>(dictionaryItem);
if (uow.Events.DispatchCancelable(DeletingDictionaryItem, this, deleteEventArgs))
{
uow.Commit();
return;
@@ -264,8 +266,8 @@ namespace Umbraco.Core.Services
var repository = RepositoryFactory.CreateDictionaryRepository(uow);
repository.Delete(dictionaryItem);
uow.Events.Dispatch(DeletedDictionaryItem, this, new DeleteEventArgs<IDictionaryItem>(dictionaryItem, false));
deleteEventArgs.CanCancel = false;
uow.Events.Dispatch(DeletedDictionaryItem, this, deleteEventArgs);
Audit(uow, AuditType.Delete, "Delete DictionaryItem performed by user", userId, dictionaryItem.Id);
uow.Commit();
@@ -336,15 +338,16 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(SavingLanguage, this, new SaveEventArgs<ILanguage>(language)))
var saveEventArgs = new SaveEventArgs<ILanguage>(language);
if (uow.Events.DispatchCancelable(SavingLanguage, this, saveEventArgs))
{
uow.Commit();
return;
}
var repository = RepositoryFactory.CreateLanguageRepository(uow);
repository.AddOrUpdate(language);
uow.Events.Dispatch(SavedLanguage, this, new SaveEventArgs<ILanguage>(language, false));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(SavedLanguage, this, saveEventArgs);
Audit(uow, AuditType.Save, "Save Language performed by user", userId, language.Id);
uow.Commit();
@@ -360,7 +363,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(DeletingLanguage, this, new DeleteEventArgs<ILanguage>(language)))
var deleteEventArgs = new DeleteEventArgs<ILanguage>(language);
if (uow.Events.DispatchCancelable(DeletingLanguage, this, deleteEventArgs))
{
uow.Commit();
return;
@@ -369,7 +373,8 @@ namespace Umbraco.Core.Services
var repository = RepositoryFactory.CreateLanguageRepository(uow);
repository.Delete(language);
uow.Events.Dispatch(DeletedLanguage, this, new DeleteEventArgs<ILanguage>(language, false));
deleteEventArgs.CanCancel = false;
uow.Events.Dispatch(DeletedLanguage, this, deleteEventArgs);
Audit(uow, AuditType.Delete, "Delete Language performed by user", userId, language.Id);
uow.Commit();
@@ -400,6 +405,15 @@ namespace Umbraco.Core.Services
}
}
public Dictionary<string, Guid> GetDictionaryItemKeyMap()
{
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
{
var repository = RepositoryFactory.CreateDictionaryRepository(uow);
return repository.GetDictionaryItemKeyMap();
}
}
#region Event Handlers
/// <summary>
/// Occurs before Delete
+8 -6
View File
@@ -144,15 +144,16 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(Deleting, this, new DeleteEventArgs<IMacro>(macro)))
var deleteEventArgs = new DeleteEventArgs<IMacro>(macro);
if (uow.Events.DispatchCancelable(Deleting, this, deleteEventArgs))
{
uow.Commit();
return;
}
var repository = RepositoryFactory.CreateMacroRepository(uow);
repository.Delete(macro);
uow.Events.Dispatch(Deleted, this, new DeleteEventArgs<IMacro>(macro, false));
deleteEventArgs.CanCancel = false;
uow.Events.Dispatch(Deleted, this, deleteEventArgs);
Audit(uow, AuditType.Delete, "Delete Macro performed by user", userId, -1);
uow.Commit();
@@ -168,7 +169,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(Saving, this, new SaveEventArgs<IMacro>(macro)))
var saveEventArgs = new SaveEventArgs<IMacro>(macro);
if (uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
{
uow.Commit();
return;
@@ -181,8 +183,8 @@ namespace Umbraco.Core.Services
var repository = RepositoryFactory.CreateMacroRepository(uow);
repository.AddOrUpdate(macro);
uow.Events.Dispatch(Saved, this, new SaveEventArgs<IMacro>(macro, false));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
Audit(uow, AuditType.Save, "Save Macro performed by user", userId, -1);
uow.Commit();
+75 -38
View File
@@ -70,7 +70,8 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(Creating, this, new NewEventArgs<IMedia>(media, mediaTypeAlias, parentId)))
var newEventArgs = new NewEventArgs<IMedia>(media, mediaTypeAlias, parentId);
if (uow.Events.DispatchCancelable(Creating, this, newEventArgs))
{
uow.Commit();
media.WasCancelled = true;
@@ -78,7 +79,8 @@ namespace Umbraco.Core.Services
}
media.CreatorId = userId;
uow.Events.Dispatch(Created, this, new NewEventArgs<IMedia>(media, false, mediaTypeAlias, parentId));
newEventArgs.CanCancel = false;
uow.Events.Dispatch(Created, this, newEventArgs);
Audit(uow, AuditType.New, string.Format("Media '{0}' was created", name), media.CreatorId, media.Id);
uow.Commit();
@@ -110,7 +112,8 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(Creating, this, new NewEventArgs<IMedia>(media, mediaTypeAlias, parent)))
var newEventArgs = new NewEventArgs<IMedia>(media, mediaTypeAlias, parent);
if (uow.Events.DispatchCancelable(Creating, this, newEventArgs))
{
uow.Commit();
media.WasCancelled = true;
@@ -118,8 +121,8 @@ namespace Umbraco.Core.Services
}
media.CreatorId = userId;
uow.Events.Dispatch(Created, this, new NewEventArgs<IMedia>(media, false, mediaTypeAlias, parent));
newEventArgs.CanCancel = false;
uow.Events.Dispatch(Created, this, newEventArgs);
Audit(uow, AuditType.New, string.Format("Media '{0}' was created", name), media.CreatorId, media.Id);
uow.Commit();
@@ -150,14 +153,16 @@ namespace Umbraco.Core.Services
{
//NOTE: I really hate the notion of these Creating/Created events - they are so inconsistent, I've only just found
// out that in these 'WithIdentity' methods, the Saving/Saved events were not fired, wtf. Anyways, they're added now.
if (uow.Events.DispatchCancelable(Creating, this, new NewEventArgs<IMedia>(media, mediaTypeAlias, parentId)))
var newEventArgs = new NewEventArgs<IMedia>(media, mediaTypeAlias, parentId);
if (uow.Events.DispatchCancelable(Creating, this, newEventArgs))
{
uow.Commit();
media.WasCancelled = true;
return media;
}
if (uow.Events.DispatchCancelable(Saving, this, new SaveEventArgs<IMedia>(media)))
var saveEventArgs = new SaveEventArgs<IMedia>(media);
if (uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
{
uow.Commit();
media.WasCancelled = true;
@@ -174,8 +179,10 @@ namespace Umbraco.Core.Services
repository.AddOrUpdatePreviewXml(media, m => _entitySerializer.Serialize(this, _dataTypeService, _userService, m));
}
uow.Events.Dispatch(Saved, this, new SaveEventArgs<IMedia>(media, false));
uow.Events.Dispatch(Created, this, new NewEventArgs<IMedia>(media, false, mediaTypeAlias, parentId));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
newEventArgs.CanCancel = false;
uow.Events.Dispatch(Created, this, newEventArgs);
Audit(uow, AuditType.New, string.Format("Media '{0}' was created with Id {1}", name, media.Id), media.CreatorId, media.Id);
uow.Commit();
@@ -208,14 +215,16 @@ namespace Umbraco.Core.Services
{
//NOTE: I really hate the notion of these Creating/Created events - they are so inconsistent, I've only just found
// out that in these 'WithIdentity' methods, the Saving/Saved events were not fired, wtf. Anyways, they're added now.
if (uow.Events.DispatchCancelable(Creating, this, new NewEventArgs<IMedia>(media, mediaTypeAlias, parent)))
var newEventArgs = new NewEventArgs<IMedia>(media, mediaTypeAlias, parent);
if (uow.Events.DispatchCancelable(Creating, this, newEventArgs))
{
uow.Commit();
media.WasCancelled = true;
return media;
}
if (uow.Events.DispatchCancelable(Saving, this, new SaveEventArgs<IMedia>(media)))
var saveEventArgs = new SaveEventArgs<IMedia>(media);
if (uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
{
uow.Commit();
media.WasCancelled = true;
@@ -232,8 +241,10 @@ namespace Umbraco.Core.Services
repository.AddOrUpdatePreviewXml(media, m => _entitySerializer.Serialize(this, _dataTypeService, _userService, m));
}
uow.Events.Dispatch(Saved, this, new SaveEventArgs<IMedia>(media, false));
uow.Events.Dispatch(Created, this, new NewEventArgs<IMedia>(media, false, mediaTypeAlias, parent));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
newEventArgs.CanCancel = false;
uow.Events.Dispatch(Created, this, newEventArgs);
Audit(uow, AuditType.New, string.Format("Media '{0}' was created with Id {1}", name, media.Id), media.CreatorId, media.Id);
uow.Commit();
@@ -786,7 +797,9 @@ namespace Umbraco.Core.Services
{
var originalPath = media.Path;
if (uow.Events.DispatchCancelable(Moving, this, new MoveEventArgs<IMedia>(new MoveEventInfo<IMedia>(media, originalPath, parentId)), "Moving"))
var moveEventInfo = new MoveEventInfo<IMedia>(media, originalPath, parentId);
var moveEventArgs = new MoveEventArgs<IMedia>(moveEventInfo);
if (uow.Events.DispatchCancelable(Moving, this, moveEventArgs, "Moving"))
{
uow.Commit();
return;
@@ -802,7 +815,7 @@ namespace Umbraco.Core.Services
//used to track all the moved entities to be given to the event
var moveInfo = new List<MoveEventInfo<IMedia>>
{
new MoveEventInfo<IMedia>(media, originalPath, parentId)
moveEventInfo
};
//Ensure that relevant properties are updated on children
@@ -816,7 +829,9 @@ namespace Umbraco.Core.Services
Save(updatedDescendants, userId, false); //no events!
}
uow.Events.Dispatch(Moved, this, new MoveEventArgs<IMedia>(false, moveInfo.ToArray()), "Moved");
moveEventArgs.MoveInfoCollection = moveInfo;
moveEventArgs.CanCancel = false;
uow.Events.Dispatch(Moved, this, moveEventArgs, "Moved");
Audit(uow, AuditType.Move, "Move Media performed by user", userId, media.Id);
uow.Commit();
@@ -858,7 +873,8 @@ namespace Umbraco.Core.Services
private Attempt<OperationStatus> DeleteUow(IScopeUnitOfWork uow, IMedia media, int userId, EventMessages evtMsgs)
{
if (uow.Events.DispatchCancelable(Deleting, this, new DeleteEventArgs<IMedia>(media, evtMsgs)))
var deleteEventArgs = new DeleteEventArgs<IMedia>(media, evtMsgs);
if (uow.Events.DispatchCancelable(Deleting, this, deleteEventArgs))
{
return OperationStatus.Cancelled(evtMsgs);
}
@@ -872,9 +888,8 @@ namespace Umbraco.Core.Services
var repository = RepositoryFactory.CreateMediaRepository(uow);
repository.Delete(media);
var args = new DeleteEventArgs<IMedia>(media, false, evtMsgs);
uow.Events.Dispatch(Deleted, this, args);
deleteEventArgs.CanCancel = false;
uow.Events.Dispatch(Deleted, this, deleteEventArgs);
Audit(uow, AuditType.Delete, "Delete Media performed by user", userId, media.Id);
@@ -893,7 +908,8 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.GetUnitOfWork())
{
if (raiseEvents && uow.Events.DispatchCancelable(Saving, this, new SaveEventArgs<IMedia>(media, evtMsgs)))
var saveEventArgs = new SaveEventArgs<IMedia>(media, evtMsgs);
if (raiseEvents && uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
{
uow.Commit();
return OperationStatus.Cancelled(evtMsgs);
@@ -921,7 +937,10 @@ namespace Umbraco.Core.Services
}
if (raiseEvents)
uow.Events.Dispatch(Saved, this, new SaveEventArgs<IMedia>(media, false, evtMsgs));
{
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
}
Audit(uow, AuditType.Save, "Save Media performed by user", userId, media.Id);
uow.Commit();
@@ -943,7 +962,8 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.GetUnitOfWork())
{
if (raiseEvents && uow.Events.DispatchCancelable(Saving, this, new SaveEventArgs<IMedia>(asArray, evtMsgs)))
var saveEventArgs = new SaveEventArgs<IMedia>(asArray, evtMsgs);
if (raiseEvents && uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
{
uow.Commit();
return OperationStatus.Cancelled(evtMsgs);
@@ -963,7 +983,10 @@ namespace Umbraco.Core.Services
}
if (raiseEvents)
uow.Events.Dispatch(Saved, this, new SaveEventArgs<IMedia>(asArray, false, evtMsgs));
{
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
}
Audit(uow, AuditType.Save, "Save Media items performed by user", userId, -1);
uow.Commit();
@@ -988,15 +1011,17 @@ namespace Umbraco.Core.Services
var files = ((MediaRepository)repository).GetFilesInRecycleBinForUploadField();
if (uow.Events.DispatchCancelable(EmptyingRecycleBin, this, new RecycleBinEventArgs(nodeObjectType, entities, files)))
var recycleBinEventArgs = new RecycleBinEventArgs(nodeObjectType, entities, files);
if (uow.Events.DispatchCancelable(EmptyingRecycleBin, this, recycleBinEventArgs))
{
uow.Commit();
return;
}
var success = repository.EmptyRecycleBin();
uow.Events.Dispatch(EmptiedRecycleBin, this, new RecycleBinEventArgs(nodeObjectType, entities, files, success));
recycleBinEventArgs.CanCancel = false;
recycleBinEventArgs.RecycleBinEmptiedSuccessfully = success;
uow.Events.Dispatch(EmptiedRecycleBin, this, recycleBinEventArgs);
Audit(uow, AuditType.Delete, "Empty Media Recycle Bin performed by user", 0, -21);
uow.Commit();
@@ -1022,7 +1047,8 @@ namespace Umbraco.Core.Services
IDictionary<string, IMedia> rootItems;
var mediaToDelete = this.TrackDeletionsForDeleteContentOfTypes(mediaTypeIds, repository, out rootItems).ToArray();
if (uow.Events.DispatchCancelable(Deleting, this, new DeleteEventArgs<IMedia>(mediaToDelete), "Deleting"))
var deleteEventArgs = new DeleteEventArgs<IMedia>(mediaToDelete);
if (uow.Events.DispatchCancelable(Deleting, this, deleteEventArgs, "Deleting"))
{
uow.Commit();
return;
@@ -1094,14 +1120,16 @@ namespace Umbraco.Core.Services
//see: http://issues.umbraco.org/issue/U4-9336
media.EnsureValidPath(Logger, entity => GetById(entity.ParentId), QuickUpdate);
var originalPath = media.Path;
if (uow.Events.DispatchCancelable(Trashing, this, new MoveEventArgs<IMedia>(new MoveEventInfo<IMedia>(media, originalPath, Constants.System.RecycleBinMedia)), "Trashing"))
var moveEventInfo = new MoveEventInfo<IMedia>(media, originalPath, Constants.System.RecycleBinMedia);
var moveEventArgs = new MoveEventArgs<IMedia>(moveEventInfo);
if (uow.Events.DispatchCancelable(Trashing, this, moveEventArgs, "Trashing"))
{
uow.Commit();
return OperationStatus.Cancelled(evtMsgs);
}
var moveInfo = new List<MoveEventInfo<IMedia>>
{
new MoveEventInfo<IMedia>(media, originalPath, Constants.System.RecycleBinMedia)
moveEventInfo
};
//get descendents to process of the content item that is being moved to trash - must be done before changing the state below,
@@ -1124,7 +1152,9 @@ namespace Umbraco.Core.Services
moveInfo.Add(new MoveEventInfo<IMedia>(descendant, descendant.Path, descendant.ParentId));
}
uow.Events.Dispatch(Trashed, this, new MoveEventArgs<IMedia>(false, evtMsgs, moveInfo.ToArray()), "Trashed");
moveEventArgs.MoveInfoCollection = moveInfo;
moveEventArgs.CanCancel = false;
uow.Events.Dispatch(Trashed, this, moveEventArgs, "Trashed");
Audit(uow, AuditType.Move, "Move Media to Recycle Bin performed by user", userId, media.Id);
uow.Commit();
@@ -1167,12 +1197,14 @@ namespace Umbraco.Core.Services
private void DeleteVersions(IScopeUnitOfWork uow, int id, DateTime versionDate, int userId = 0)
{
if (uow.Events.DispatchCancelable(DeletingVersions, this, new DeleteRevisionsEventArgs(id, dateToRetain: versionDate)))
var deleteRevisionsEventArgs = new DeleteRevisionsEventArgs(id, dateToRetain: versionDate);
if (uow.Events.DispatchCancelable(DeletingVersions, this, deleteRevisionsEventArgs))
return;
var repository = RepositoryFactory.CreateMediaRepository(uow);
repository.DeleteVersions(id, versionDate);
uow.Events.Dispatch(DeletedVersions, this, new DeleteRevisionsEventArgs(id, false, dateToRetain: versionDate));
deleteRevisionsEventArgs.CanCancel = false;
uow.Events.Dispatch(DeletedVersions, this, deleteRevisionsEventArgs);
Audit(uow, AuditType.Delete, "Delete Media by version date performed by user", userId, -1);
}
@@ -1188,7 +1220,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(DeletingVersions, this, new DeleteRevisionsEventArgs(id, specificVersion: versionId)))
var deleteRevisionsEventArgs = new DeleteRevisionsEventArgs(id, specificVersion: versionId);
if (uow.Events.DispatchCancelable(DeletingVersions, this, deleteRevisionsEventArgs))
{
uow.Commit();
return;
@@ -1202,8 +1235,8 @@ namespace Umbraco.Core.Services
var repository = RepositoryFactory.CreateMediaRepository(uow);
repository.DeleteVersion(versionId);
uow.Events.Dispatch(DeletedVersions, this, new DeleteRevisionsEventArgs(id, false, specificVersion: versionId));
deleteRevisionsEventArgs.CanCancel = false;
uow.Events.Dispatch(DeletedVersions, this, deleteRevisionsEventArgs);
Audit(uow, AuditType.Delete, "Delete Media by version performed by user", userId, -1);
uow.Commit();
@@ -1246,7 +1279,8 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.GetUnitOfWork())
{
if (raiseEvents && uow.Events.DispatchCancelable(Saving, this, new SaveEventArgs<IMedia>(asArray)))
var saveEventArgs = new SaveEventArgs<IMedia>(asArray);
if (raiseEvents && uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
{
uow.Commit();
return false;
@@ -1278,7 +1312,10 @@ namespace Umbraco.Core.Services
}
if (raiseEvents)
uow.Events.Dispatch(Saved, this, new SaveEventArgs<IMedia>(asArray, false));
{
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
}
Audit(uow, AuditType.Sort, "Sorting Media performed by user", userId, 0);
uow.Commit();
@@ -71,7 +71,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (raiseEvents && uow.Events.DispatchCancelable(Saving, this, new SaveEventArgs<IMemberGroup>(memberGroup)))
var saveEventArgs = new SaveEventArgs<IMemberGroup>(memberGroup);
if (raiseEvents && uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
{
uow.Commit();
return;
@@ -81,17 +82,19 @@ namespace Umbraco.Core.Services
repository.AddOrUpdate(memberGroup);
uow.Commit();
if (raiseEvents)
uow.Events.Dispatch(Saved, this, new SaveEventArgs<IMemberGroup>(memberGroup, false));
{
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
}
}
}
public void Delete(IMemberGroup memberGroup)
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(Deleting, this, new DeleteEventArgs<IMemberGroup>(memberGroup)))
var deleteEventArgs = new DeleteEventArgs<IMemberGroup>(memberGroup);
if (uow.Events.DispatchCancelable(Deleting, this, deleteEventArgs))
{
uow.Commit();
return;
@@ -99,7 +102,8 @@ namespace Umbraco.Core.Services
var repository = RepositoryFactory.CreateMemberGroupRepository(uow);
repository.Delete(memberGroup);
uow.Commit();
uow.Events.Dispatch(Deleted, this, new DeleteEventArgs<IMemberGroup>(memberGroup, false));
deleteEventArgs.CanCancel = false;
uow.Events.Dispatch(Deleted, this, deleteEventArgs);
}
}
+29 -14
View File
@@ -25,8 +25,11 @@ namespace Umbraco.Core.Services
{
private readonly IMemberGroupService _memberGroupService;
private readonly EntityXmlSerializer _entitySerializer = new EntityXmlSerializer();
private readonly IDataTypeService _dataTypeService;
private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim();
private readonly IDataTypeService _dataTypeService;
private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim();
//only for unit tests!
internal MembershipProviderBase MembershipProvider { get; set; }
public MemberService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory, IMemberGroupService memberGroupService, IDataTypeService dataTypeService)
: base(provider, repositoryFactory, logger, eventMessagesFactory)
@@ -35,7 +38,7 @@ namespace Umbraco.Core.Services
if (dataTypeService == null) throw new ArgumentNullException("dataTypeService");
_memberGroupService = memberGroupService;
_dataTypeService = dataTypeService;
}
}
#region IMemberService Implementation
@@ -91,7 +94,7 @@ namespace Umbraco.Core.Services
{
if (member == null) throw new ArgumentNullException("member");
var provider = MembershipProviderExtensions.GetMembersMembershipProvider();
var provider = MembershipProvider ?? MembershipProviderExtensions.GetMembersMembershipProvider();
if (provider.IsUmbracoMembershipProvider())
{
provider.ChangePassword(member.Username, "", password);
@@ -234,7 +237,8 @@ namespace Umbraco.Core.Services
var query = Query<IMember>.Builder.Where(x => x.ContentTypeId == memberTypeId);
var members = repository.GetByQuery(query).ToArray();
if (uow.Events.DispatchCancelable(Deleting, this, new DeleteEventArgs<IMember>(members)))
var deleteEventArgs = new DeleteEventArgs<IMember>(members);
if (uow.Events.DispatchCancelable(Deleting, this, deleteEventArgs))
{
uow.Commit();
return;
@@ -862,7 +866,8 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(Saving, this, new SaveEventArgs<IMember>(member)))
var saveEventArgs = new SaveEventArgs<IMember>(member);
if (uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
{
uow.Commit();
member.WasCancelled = true;
@@ -881,7 +886,8 @@ namespace Umbraco.Core.Services
uow.Commit();
uow.Events.Dispatch(Saved, this, new SaveEventArgs<IMember>(member, false));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
uow.Events.Dispatch(Created, this, new NewEventArgs<IMember>(member, false, memberType.Alias, -1));
}
@@ -958,7 +964,8 @@ namespace Umbraco.Core.Services
private void Delete(IScopeUnitOfWork uow, IMember member)
{
if (uow.Events.DispatchCancelable(Deleting, this, new DeleteEventArgs<IMember>(member)))
var deleteEventArgs = new DeleteEventArgs<IMember>(member);
if (uow.Events.DispatchCancelable(Deleting, this, deleteEventArgs))
{
uow.Commit();
return;
@@ -968,8 +975,8 @@ namespace Umbraco.Core.Services
repository.Delete(member);
uow.Commit();
var args = new DeleteEventArgs<IMember>(member, false);
uow.Events.Dispatch(Deleted, this, args);
deleteEventArgs.CanCancel = false;
uow.Events.Dispatch(Deleted, this, deleteEventArgs);
}
/// <summary>
@@ -982,9 +989,10 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
var saveEventArgs = new SaveEventArgs<IMember>(entity);
if (raiseEvents)
{
if (uow.Events.DispatchCancelable(Saving, this, new SaveEventArgs<IMember>(entity)))
if (uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
{
uow.Commit();
return;
@@ -1008,7 +1016,10 @@ namespace Umbraco.Core.Services
uow.Commit();
if (raiseEvents)
uow.Events.Dispatch(Saved, this, new SaveEventArgs<IMember>(entity, false));
{
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
}
}
}
@@ -1027,7 +1038,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (raiseEvents && uow.Events.DispatchCancelable(Saving, this, new SaveEventArgs<IMember>(asArray)))
var saveEventArgs = new SaveEventArgs<IMember>(asArray);
if (raiseEvents && uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
{
uow.Commit();
return;
@@ -1049,7 +1061,10 @@ namespace Umbraco.Core.Services
uow.Commit();
if (raiseEvents)
uow.Events.Dispatch(Saved, this, new SaveEventArgs<IMember>(asArray, false));
{
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
}
}
+18 -12
View File
@@ -80,7 +80,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(Saving, this, new SaveEventArgs<IMemberType>(memberType)))
var saveEventArgs = new SaveEventArgs<IMemberType>(memberType);
if (uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
{
uow.Commit();
return;
@@ -101,7 +102,8 @@ namespace Umbraco.Core.Services
UpdateContentXmlStructure(memberType);
uow.Commit(); // actually commit uow
uow.Events.Dispatch(Saved, this, new SaveEventArgs<IMemberType>(memberType, false));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
}
}
}
@@ -114,7 +116,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(Saving, this, new SaveEventArgs<IMemberType>(asArray)))
var saveEventArgs = new SaveEventArgs<IMemberType>(asArray);
if (uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
{
uow.Commit();
return;
@@ -131,8 +134,8 @@ namespace Umbraco.Core.Services
UpdateContentXmlStructure(asArray.Cast<IContentTypeBase>().ToArray());
uow.Commit(); // actually commit uow
uow.Events.Dispatch(Saved, this, new SaveEventArgs<IMemberType>(asArray, false));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
}
}
@@ -142,10 +145,11 @@ namespace Umbraco.Core.Services
{
using (new WriteLock(Locker))
{
var deleteEventArgs = new DeleteEventArgs<IMemberType>(memberType);
using (var scope = UowProvider.ScopeProvider.CreateScope())
{
scope.Complete(); // always
if (scope.Events.DispatchCancelable(Deleting, this, new DeleteEventArgs<IMemberType>(memberType)))
scope.Complete(); // always
if (scope.Events.DispatchCancelable(Deleting, this, deleteEventArgs))
return;
}
@@ -157,7 +161,8 @@ namespace Umbraco.Core.Services
repository.Delete(memberType);
uow.Commit();
uow.Events.Dispatch(Deleted, this, new DeleteEventArgs<IMemberType>(memberType, false));
deleteEventArgs.CanCancel = false;
uow.Events.Dispatch(Deleted, this, deleteEventArgs);
}
}
}
@@ -168,10 +173,11 @@ namespace Umbraco.Core.Services
using (new WriteLock(Locker))
{
var deleteEventArgs = new DeleteEventArgs<IMemberType>(asArray);
using (var scope = UowProvider.ScopeProvider.CreateScope())
{
scope.Complete(); // always
if (scope.Events.DispatchCancelable(Deleting, this, new DeleteEventArgs<IMemberType>(asArray)))
scope.Complete(); // always
if (scope.Events.DispatchCancelable(Deleting, this, deleteEventArgs))
return;
}
@@ -189,8 +195,8 @@ namespace Umbraco.Core.Services
}
uow.Commit();
uow.Events.Dispatch(Deleted, this, new DeleteEventArgs<IMemberType>(asArray, false));
deleteEventArgs.CanCancel = false;
uow.Events.Dispatch(Deleted, this, deleteEventArgs);
}
}
}
@@ -148,7 +148,8 @@ namespace Umbraco.Core.Services
return Attempt<OperationStatus<PublicAccessEntry, OperationStatusType>>.Succeed(new OperationStatus<PublicAccessEntry, OperationStatusType>(entry, OperationStatusType.Success, evtMsgs));
}
if (uow.Events.DispatchCancelable(Saving, this, new SaveEventArgs<PublicAccessEntry>(entry, evtMsgs)))
var saveEventArgs = new SaveEventArgs<PublicAccessEntry>(entry, evtMsgs);
if (uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
{
uow.Commit();
return Attempt<OperationStatus<PublicAccessEntry, OperationStatusType>>.Fail(
@@ -158,8 +159,8 @@ namespace Umbraco.Core.Services
repo.AddOrUpdate(entry);
uow.Commit();
uow.Events.Dispatch(Saved, this, new SaveEventArgs<PublicAccessEntry>(entry, false, evtMsgs));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
return Attempt<OperationStatus<PublicAccessEntry, OperationStatusType>>.Succeed(
new OperationStatus<PublicAccessEntry, OperationStatusType>(entry, OperationStatusType.Success, evtMsgs));
}
@@ -193,7 +194,8 @@ namespace Umbraco.Core.Services
entry.RemoveRule(existingRule);
if (uow.Events.DispatchCancelable(Saving, this, new SaveEventArgs<PublicAccessEntry>(entry, evtMsgs)))
var saveEventArgs = new SaveEventArgs<PublicAccessEntry>(entry, evtMsgs);
if (uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
{
uow.Commit();
return OperationStatus.Cancelled(evtMsgs);
@@ -202,8 +204,8 @@ namespace Umbraco.Core.Services
repo.AddOrUpdate(entry);
uow.Commit();
uow.Events.Dispatch(Saved, this, new SaveEventArgs<PublicAccessEntry>(entry, false, evtMsgs));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
return OperationStatus.Success(evtMsgs);
}
@@ -219,7 +221,8 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(Saving, this, new SaveEventArgs<PublicAccessEntry>(entry, evtMsgs)))
var saveEventArgs = new SaveEventArgs<PublicAccessEntry>(entry, evtMsgs);
if (uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
{
uow.Commit();
return OperationStatus.Cancelled(evtMsgs);
@@ -228,7 +231,8 @@ namespace Umbraco.Core.Services
var repo = RepositoryFactory.CreatePublicAccessRepository(uow);
repo.AddOrUpdate(entry);
uow.Commit();
uow.Events.Dispatch(Saved, this, new SaveEventArgs<PublicAccessEntry>(entry, false, evtMsgs));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
return OperationStatus.Success(evtMsgs);
}
@@ -245,7 +249,8 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(Deleting, this, new DeleteEventArgs<PublicAccessEntry>(entry, evtMsgs)))
var deleteEventArgs = new DeleteEventArgs<PublicAccessEntry>(entry, evtMsgs);
if (uow.Events.DispatchCancelable(Deleting, this, deleteEventArgs))
{
uow.Commit();
return OperationStatus.Cancelled(evtMsgs);
@@ -254,7 +259,8 @@ namespace Umbraco.Core.Services
var repo = RepositoryFactory.CreatePublicAccessRepository(uow);
repo.Delete(entry);
uow.Commit();
uow.Events.Dispatch(Deleted, this, new DeleteEventArgs<PublicAccessEntry>(entry, false, evtMsgs));
deleteEventArgs.CanCancel = false;
uow.Events.Dispatch(Deleted, this, deleteEventArgs);
return OperationStatus.Success(evtMsgs);
}
@@ -72,10 +72,28 @@ namespace Umbraco.Core.Services
public static bool HasAccess(this IPublicAccessService publicAccessService, string path, MembershipUser member, RoleProvider roleProvider)
{
return publicAccessService.HasAccess(path, member.UserName, roleProvider.GetRolesForUser);
}
/// <summary>
/// Checks if the member with the specified username has access to the path which is also based on the passed in roles for the member
/// </summary>
/// <param name="publicAccessService"></param>
/// <param name="path"></param>
/// <param name="username"></param>
/// <param name="rolesCallback">A callback to retrieve the roles for this member</param>
/// <returns></returns>
public static bool HasAccess(this IPublicAccessService publicAccessService, string path, string username, Func<string, IEnumerable<string>> rolesCallback)
{
if (rolesCallback == null) throw new ArgumentNullException("roles");
if (string.IsNullOrWhiteSpace(username)) throw new ArgumentException("Value cannot be null or whitespace.", "username");
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Value cannot be null or whitespace.", "path");
var entry = publicAccessService.GetEntryForContent(path.EnsureEndsWith(path));
if (entry == null) return true;
var roles = roleProvider.GetRolesForUser(member.UserName);
var roles = rolesCallback(username);
return entry.Rules.Any(x => x.RuleType == Constants.Conventions.PublicAccess.MemberRoleRuleType
&& roles.Contains(x.RuleValue));
}
+24 -13
View File
@@ -412,7 +412,8 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateRelationRepository(uow);
if (uow.Events.DispatchCancelable(SavingRelation, this, new SaveEventArgs<IRelation>(relation)))
var saveEventArgs = new SaveEventArgs<IRelation>(relation);
if (uow.Events.DispatchCancelable(SavingRelation, this, saveEventArgs))
{
uow.Commit();
return relation;
@@ -420,7 +421,8 @@ namespace Umbraco.Core.Services
repository.AddOrUpdate(relation);
uow.Commit();
uow.Events.Dispatch(SavedRelation, this, new SaveEventArgs<IRelation>(relation, false));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(SavedRelation, this, saveEventArgs);
return relation;
}
}
@@ -442,7 +444,8 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(SavingRelation, this, new SaveEventArgs<IRelation>(relation)))
var saveEventArgs = new SaveEventArgs<IRelation>(relation);
if (uow.Events.DispatchCancelable(SavingRelation, this, saveEventArgs))
{
uow.Commit();
return relation;
@@ -450,8 +453,8 @@ namespace Umbraco.Core.Services
var repository = RepositoryFactory.CreateRelationRepository(uow);
repository.AddOrUpdate(relation);
uow.Commit();
uow.Events.Dispatch(SavedRelation, this, new SaveEventArgs<IRelation>(relation, false));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(SavedRelation, this, saveEventArgs);
return relation;
}
}
@@ -568,7 +571,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(SavingRelation, this, new SaveEventArgs<IRelation>(relation)))
var saveEventArgs = new SaveEventArgs<IRelation>(relation);
if (uow.Events.DispatchCancelable(SavingRelation, this, saveEventArgs))
{
uow.Commit();
return;
@@ -576,7 +580,8 @@ namespace Umbraco.Core.Services
var repository = RepositoryFactory.CreateRelationRepository(uow);
repository.AddOrUpdate(relation);
uow.Commit();
uow.Events.Dispatch(SavedRelation, this, new SaveEventArgs<IRelation>(relation, false));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(SavedRelation, this, saveEventArgs);
}
}
@@ -588,7 +593,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(SavingRelationType, this, new SaveEventArgs<IRelationType>(relationType)))
var saveEventArgs = new SaveEventArgs<IRelationType>(relationType);
if (uow.Events.DispatchCancelable(SavingRelationType, this, saveEventArgs))
{
uow.Commit();
return;
@@ -596,7 +602,8 @@ namespace Umbraco.Core.Services
var repository = RepositoryFactory.CreateRelationTypeRepository(uow);
repository.AddOrUpdate(relationType);
uow.Commit();
uow.Events.Dispatch(SavedRelationType, this, new SaveEventArgs<IRelationType>(relationType, false));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(SavedRelationType, this, saveEventArgs);
}
}
@@ -608,7 +615,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(DeletingRelation, this, new DeleteEventArgs<IRelation>(relation)))
var deleteEventArgs = new DeleteEventArgs<IRelation>(relation);
if (uow.Events.DispatchCancelable(DeletingRelation, this, deleteEventArgs))
{
uow.Commit();
return;
@@ -616,7 +624,8 @@ namespace Umbraco.Core.Services
var repository = RepositoryFactory.CreateRelationRepository(uow);
repository.Delete(relation);
uow.Commit();
uow.Events.Dispatch(DeletedRelation, this, new DeleteEventArgs<IRelation>(relation, false));
deleteEventArgs.CanCancel = false;
uow.Events.Dispatch(DeletedRelation, this, deleteEventArgs);
}
}
@@ -628,7 +637,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(DeletingRelationType, this, new DeleteEventArgs<IRelationType>(relationType)))
var deleteEventArgs = new DeleteEventArgs<IRelationType>(relationType);
if (uow.Events.DispatchCancelable(DeletingRelationType, this, deleteEventArgs))
{
uow.Commit();
return;
@@ -636,7 +646,8 @@ namespace Umbraco.Core.Services
var repository = RepositoryFactory.CreateRelationTypeRepository(uow);
repository.Delete(relationType);
uow.Commit();
uow.Events.Dispatch(DeletedRelationType, this, new DeleteEventArgs<IRelationType>(relationType, false));
deleteEventArgs.CanCancel = false;
uow.Events.Dispatch(DeletedRelationType, this, deleteEventArgs);
}
}
+30 -13
View File
@@ -178,7 +178,8 @@ namespace Umbraco.Core.Services
user.AddAllowedSection("content");
user.AddAllowedSection("media");
if (uow.Events.DispatchCancelable(SavingUser, this, new SaveEventArgs<IUser>(user)))
var saveEventArgs = new SaveEventArgs<IUser>(user);
if (uow.Events.DispatchCancelable(SavingUser, this, saveEventArgs))
{
uow.Commit();
return user;
@@ -186,8 +187,8 @@ namespace Umbraco.Core.Services
repository.AddOrUpdate(user);
uow.Commit();
uow.Events.Dispatch(SavedUser, this, new SaveEventArgs<IUser>(user, false));
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(SavedUser, this, saveEventArgs);
return user;
}
@@ -314,7 +315,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(DeletingUser, this, new DeleteEventArgs<IUser>(user)))
var deleteEventArgs = new DeleteEventArgs<IUser>(user);
if (uow.Events.DispatchCancelable(DeletingUser, this, deleteEventArgs))
{
uow.Commit();
return;
@@ -322,7 +324,8 @@ namespace Umbraco.Core.Services
var repository = RepositoryFactory.CreateUserRepository(uow);
repository.Delete(user);
uow.Commit();
uow.Events.Dispatch(DeletedUser, this, new DeleteEventArgs<IUser>(user, false));
deleteEventArgs.CanCancel = false;
uow.Events.Dispatch(DeletedUser, this, deleteEventArgs);
}
}
}
@@ -337,7 +340,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (raiseEvents && uow.Events.DispatchCancelable(SavingUser, this, new SaveEventArgs<IUser>(entity)))
var saveEventArgs = new SaveEventArgs<IUser>(entity);
if (raiseEvents && uow.Events.DispatchCancelable(SavingUser, this, saveEventArgs))
{
uow.Commit();
return;
@@ -363,7 +367,10 @@ namespace Umbraco.Core.Services
uow.Commit();
if (raiseEvents)
uow.Events.Dispatch(SavedUser, this, new SaveEventArgs<IUser>(entity, false));
{
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(SavedUser, this, saveEventArgs);
}
}
catch (DbException ex)
{
@@ -391,9 +398,10 @@ namespace Umbraco.Core.Services
var asArray = entities.ToArray();
using (var uow = UowProvider.GetUnitOfWork())
{
var saveEventArgs = new SaveEventArgs<IUser>(asArray);
if (raiseEvents)
{
if (uow.Events.DispatchCancelable(SavingUser, this, new SaveEventArgs<IUser>(asArray)))
if (uow.Events.DispatchCancelable(SavingUser, this, saveEventArgs))
{
uow.Commit();
return;
@@ -416,7 +424,10 @@ namespace Umbraco.Core.Services
uow.Commit();
if (raiseEvents)
uow.Events.Dispatch(SavedUser, this, new SaveEventArgs<IUser>(asArray, false));
{
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(SavedUser, this, saveEventArgs);
}
}
@@ -719,7 +730,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (raiseEvents && uow.Events.DispatchCancelable(SavingUserType, this, new SaveEventArgs<IUserType>(userType)))
var saveEventArgs = new SaveEventArgs<IUserType>(userType);
if (raiseEvents && uow.Events.DispatchCancelable(SavingUserType, this, saveEventArgs))
{
uow.Commit();
return;
@@ -729,7 +741,10 @@ namespace Umbraco.Core.Services
repository.AddOrUpdate(userType);
uow.Commit();
if (raiseEvents)
uow.Events.Dispatch(SavedUserType, this, new SaveEventArgs<IUserType>(userType, false));
{
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(SavedUserType, this, saveEventArgs);
}
}
}
@@ -741,7 +756,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.GetUnitOfWork())
{
if (uow.Events.DispatchCancelable(DeletingUserType, this, new DeleteEventArgs<IUserType>(userType)))
var deleteEventArgs = new DeleteEventArgs<IUserType>(userType);
if (uow.Events.DispatchCancelable(DeletingUserType, this, deleteEventArgs))
{
uow.Commit();
return;
@@ -749,7 +765,8 @@ namespace Umbraco.Core.Services
var repository = RepositoryFactory.CreateUserTypeRepository(uow);
repository.Delete(userType);
uow.Commit();
uow.Events.Dispatch(DeletedUserType, this, new DeleteEventArgs<IUserType>(userType, false));
deleteEventArgs.CanCancel = false;
uow.Events.Dispatch(DeletedUserType, this, deleteEventArgs);
}
}
+2 -2
View File
@@ -759,7 +759,7 @@ namespace Umbraco.Core
foreach (var b in hashedByteArray)
{
//append it to our StringBuilder
stringBuilder.Append(b.ToString("x2").ToLower());
stringBuilder.Append(b.ToString("x2"));
}
//return the hashed value
@@ -874,7 +874,7 @@ namespace Umbraco.Core
}
/// <summary>
/// Ensures that the folder path endds with a DirectorySeperatorChar
/// Ensures that the folder path ends with a DirectorySeperatorChar
/// </summary>
/// <param name="currentFolder"></param>
/// <returns></returns>
+6 -6
View File
@@ -57,17 +57,18 @@ namespace Umbraco.Core
public new static StringUdi Parse(string s)
{
var udi = Udi.Parse(s);
if (!(udi is StringUdi))
if (udi is StringUdi == false)
throw new FormatException("String \"" + s + "\" is not a string entity id.");
return (StringUdi)udi;
return (StringUdi) udi;
}
public static bool TryParse(string s, out StringUdi udi)
{
udi = null;
Udi tmp;
if (!TryParse(s, out tmp) || !(tmp is StringUdi)) return false;
udi = (StringUdi)tmp;
if (TryParse(s, out tmp) == false || tmp is StringUdi == false) return false;
udi = (StringUdi) tmp;
return true;
}
@@ -77,10 +78,9 @@ namespace Umbraco.Core
get { return Id == string.Empty; }
}
/// <inheritdoc/>
public StringUdi EnsureClosed()
{
base.EnsureNotRoot();
EnsureNotRoot();
return this;
}
}
@@ -126,10 +126,12 @@ namespace Umbraco.Core.Sync
public static string GetServerHash(string machineName, string appDomainAppId)
{
var hasher = new HashCodeCombiner();
hasher.AddCaseInsensitiveString(appDomainAppId);
hasher.AddCaseInsensitiveString(machineName);
return hasher.GetCombinedHashCode();
using (var generator = new HashGenerator())
{
generator.AddString(machineName);
generator.AddString(appDomainAppId);
return generator.GenerateHash();
}
}
protected override bool RequiresDistributed(IEnumerable<IServerAddress> servers, ICacheRefresher refresher, MessageType messageType)
+153 -58
View File
@@ -3,7 +3,6 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using Umbraco.Core.Deploy;
namespace Umbraco.Core
@@ -15,7 +14,15 @@ namespace Umbraco.Core
[TypeConverter(typeof(UdiTypeConverter))]
public abstract class Udi : IComparable<Udi>
{
private static readonly Lazy<Dictionary<string, UdiType>> UdiTypes;
// notes - see U4-10409
// if this class is used during application pre-start it cannot scans the assemblies,
// this is addressed by lazily-scanning, with the following caveats:
// - parsing a root udi still requires a scan and therefore still breaks
// - parsing an invalid udi ("umb://should-be-guid/<not-a-guid>") corrupts KnowUdiTypes
private static volatile bool _scanned;
private static readonly object ScanLocker = new object();
private static ConcurrentDictionary<string, UdiType> _udiTypes;
private static readonly ConcurrentDictionary<string, Udi> RootUdis = new ConcurrentDictionary<string, Udi>();
internal readonly Uri UriValue; // internal for UdiRange
@@ -42,50 +49,16 @@ namespace Umbraco.Core
static Udi()
{
UdiTypes = new Lazy<Dictionary<string, UdiType>>(() =>
{
var result = new Dictionary<string, UdiType>();
// known types:
foreach (var fi in typeof(Constants.UdiEntityType).GetFields(BindingFlags.Public | BindingFlags.Static))
{
// IsLiteral determines if its value is written at
// compile time and not changeable
// IsInitOnly determine if the field can be set
// in the body of the constructor
// for C# a field which is readonly keyword would have both true
// but a const field would have only IsLiteral equal to true
if (fi.IsLiteral && fi.IsInitOnly == false)
{
var udiType = fi.GetCustomAttribute<Constants.UdiTypeAttribute>();
// initialize with known (built-in) Udi types
// we will add scanned types later on
_udiTypes = new ConcurrentDictionary<string, UdiType>(Constants.UdiEntityType.GetTypes());
}
if (udiType == null)
throw new InvalidOperationException("All Constants listed in UdiEntityType must be attributed with " + typeof(Constants.UdiTypeAttribute));
result[fi.GetValue(null).ToString()] = udiType.UdiType;
}
}
// Scan for unknown UDI types
// there is no way we can get the "registered" service connectors, as registration
// happens in Deploy, not in Core, and the Udi class belongs to Core - therefore, we
// just pick every service connectors - just making sure that not two of them
// would register the same entity type, with different udi types (would not make
// much sense anyways).
var connectors = PluginManager.Current.ResolveTypes<IServiceConnector>();
foreach (var connector in connectors)
{
var attrs = connector.GetCustomAttributes<UdiDefinitionAttribute>(false);
foreach (var attr in attrs)
{
UdiType udiType;
if (result.TryGetValue(attr.EntityType, out udiType) && udiType != attr.UdiType)
throw new Exception(string.Format("Entity type \"{0}\" is declared by more than one IServiceConnector, with different UdiTypes.", attr.EntityType));
result[attr.EntityType] = attr.UdiType;
}
}
return result;
});
// for tests, totally unsafe
internal static void ResetUdiTypes()
{
_udiTypes = new ConcurrentDictionary<string, UdiType>(Constants.UdiEntityType.GetTypes());
_scanned = false;
}
/// <summary>
@@ -113,17 +86,65 @@ namespace Umbraco.Core
public static Udi Parse(string s)
{
Udi udi;
ParseInternal(s, false, out udi);
ParseInternal(s, false, false, out udi);
return udi;
}
public static bool TryParse(string s, out Udi udi)
/// <summary>
/// Converts the string representation of an entity identifier into the equivalent Udi instance.
/// </summary>
/// <param name="s">The string to convert.</param>
/// <param name="knownTypes">A value indicating whether to only deal with known types.</param>
/// <returns>An Udi instance that contains the value that was parsed.</returns>
/// <remarks>
/// <para>If <paramref name="knownTypes"/> is <c>true</c>, and the string could not be parsed because
/// the entity type was not known, the method succeeds but sets <c>udi</c>to an
/// <see cref="UnknownTypeUdi"/> value.</para>
/// <para>If <paramref name="knownTypes"/> is <c>true</c>, assemblies are not scanned for types,
/// and therefore only builtin types may be known. Unless scanning already took place.</para>
/// </remarks>
public static Udi Parse(string s, bool knownTypes)
{
return ParseInternal(s, true, out udi);
Udi udi;
ParseInternal(s, false, knownTypes, out udi);
return udi;
}
private static bool ParseInternal(string s, bool tryParse, out Udi udi)
/// <summary>
/// Converts the string representation of an entity identifier into the equivalent Udi instance.
/// </summary>
/// <param name="s">The string to convert.</param>
/// <param name="udi">An Udi instance that contains the value that was parsed.</param>
/// <returns>A boolean value indicating whether the string could be parsed.</returns>
public static bool TryParse(string s, out Udi udi)
{
return ParseInternal(s, true, false, out udi);
}
/// <summary>
/// Converts the string representation of an entity identifier into the equivalent Udi instance.
/// </summary>
/// <param name="s">The string to convert.</param>
/// <param name="knownTypes">A value indicating whether to only deal with known types.</param>
/// <param name="udi">An Udi instance that contains the value that was parsed.</param>
/// <returns>A boolean value indicating whether the string could be parsed.</returns>
/// <remarks>
/// <para>If <paramref name="knownTypes"/> is <c>true</c>, and the string could not be parsed because
/// the entity type was not known, the method returns <c>false</c> but still sets <c>udi</c>
/// to an <see cref="UnknownTypeUdi"/> value.</para>
/// <para>If <paramref name="knownTypes"/> is <c>true</c>, assemblies are not scanned for types,
/// and therefore only builtin types may be known. Unless scanning already took place.</para>
/// </remarks>
public static bool TryParse(string s, bool knownTypes, out Udi udi)
{
return ParseInternal(s, true, knownTypes, out udi);
}
private static bool ParseInternal(string s, bool tryParse, bool knownTypes, out Udi udi)
{
if (knownTypes == false)
EnsureScanForUdiTypes();
udi = null;
Uri uri;
@@ -136,12 +157,21 @@ namespace Umbraco.Core
var entityType = uri.Host;
UdiType udiType;
if (UdiTypes.Value.TryGetValue(entityType, out udiType) == false)
if (_udiTypes.TryGetValue(entityType, out udiType) == false)
{
if (knownTypes)
{
// not knowing the type is not an error
// just return the unknown type udi
udi = UnknownTypeUdi.Instance;
return false;
}
if (tryParse) return false;
throw new FormatException(string.Format("Unknown entity type \"{0}\".", entityType));
}
var path = uri.AbsolutePath.TrimStart('/');
if (udiType == UdiType.GuidUdi)
{
if (path == string.Empty)
@@ -158,21 +188,25 @@ namespace Umbraco.Core
udi = new GuidUdi(uri.Host, guid);
return true;
}
if (udiType == UdiType.StringUdi)
{
udi = path == string.Empty ? GetRootUdi(uri.Host) : new StringUdi(uri.Host, Uri.UnescapeDataString(path));
return true;
}
if (tryParse) return false;
throw new InvalidOperationException("Internal error.");
throw new InvalidOperationException(string.Format("Invalid udi type \"{0}\".", udiType));
}
private static Udi GetRootUdi(string entityType)
{
EnsureScanForUdiTypes();
return RootUdis.GetOrAdd(entityType, x =>
{
UdiType udiType;
if (UdiTypes.Value.TryGetValue(x, out udiType) == false)
if (_udiTypes.TryGetValue(x, out udiType) == false)
throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", entityType));
return udiType == UdiType.StringUdi
? (Udi)new StringUdi(entityType, string.Empty)
@@ -180,6 +214,46 @@ namespace Umbraco.Core
});
}
/// <summary>
/// When required scan assemblies for known UDI types based on <see cref="IServiceConnector"/> instances
/// </summary>
/// <remarks>
/// This is only required when needing to resolve root udis
/// </remarks>
private static void EnsureScanForUdiTypes()
{
if (_scanned) return;
lock (ScanLocker)
{
// Scan for unknown UDI types
// there is no way we can get the "registered" service connectors, as registration
// happens in Deploy, not in Core, and the Udi class belongs to Core - therefore, we
// just pick every service connectors - just making sure that not two of them
// would register the same entity type, with different udi types (would not make
// much sense anyways).
var connectors = PluginManager.Current.ResolveTypes<IServiceConnector>();
var result = new Dictionary<string, UdiType>();
foreach (var connector in connectors)
{
var attrs = connector.GetCustomAttributes<UdiDefinitionAttribute>(false);
foreach (var attr in attrs)
{
UdiType udiType;
if (result.TryGetValue(attr.EntityType, out udiType) && udiType != attr.UdiType)
throw new Exception(string.Format("Entity type \"{0}\" is declared by more than one IServiceConnector, with different UdiTypes.", attr.EntityType));
result[attr.EntityType] = attr.UdiType;
}
}
// merge these into the known list
foreach (var item in result)
_udiTypes.TryAdd(item.Key, item.Value);
_scanned = true;
}
}
/// <summary>
/// Creates a root Udi for an entity type.
/// </summary>
@@ -199,13 +273,14 @@ namespace Umbraco.Core
public static Udi Create(string entityType, string id)
{
UdiType udiType;
if (UdiTypes.Value.TryGetValue(entityType, out udiType) == false)
if (_udiTypes.TryGetValue(entityType, out udiType) == false)
throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", entityType), "entityType");
if (string.IsNullOrWhiteSpace(id))
throw new ArgumentException("Value cannot be null or whitespace.", "id");
if (udiType != UdiType.StringUdi)
throw new InvalidOperationException(string.Format("Entity type \"{0}\" does not have string udis.", entityType));
return new StringUdi(entityType, id);
}
@@ -218,24 +293,31 @@ namespace Umbraco.Core
public static Udi Create(string entityType, Guid id)
{
UdiType udiType;
if (UdiTypes.Value.TryGetValue(entityType, out udiType) == false)
if (_udiTypes.TryGetValue(entityType, out udiType) == false)
throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", entityType), "entityType");
if (udiType != UdiType.GuidUdi)
throw new InvalidOperationException(string.Format("Entity type \"{0}\" does not have guid udis.", entityType));
throw new InvalidOperationException(string.Format("Entity type \"{0}\" does not have guid udis.", entityType));
if (id == default(Guid))
throw new ArgumentException("Cannot be an empty guid.", "id");
return new GuidUdi(entityType, id);
}
internal static Udi Create(Uri uri)
{
// if it's a know type go fast and use ctors
// else fallback to parsing the string (and guess the type)
UdiType udiType;
if (UdiTypes.Value.TryGetValue(uri.Host, out udiType) == false)
if (_udiTypes.TryGetValue(uri.Host, out udiType) == false)
throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", uri.Host), "uri");
if (udiType == UdiType.GuidUdi)
return new GuidUdi(uri);
if (udiType == UdiType.GuidUdi)
return new StringUdi(uri);
throw new ArgumentException(string.Format("Uri \"{0}\" is not a valid udi.", uri));
}
@@ -284,6 +366,19 @@ namespace Umbraco.Core
{
return (udi1 == udi2) == false;
}
}
private class UnknownTypeUdi : Udi
{
private UnknownTypeUdi()
: base("unknown", "umb://unknown/")
{ }
public static readonly UnknownTypeUdi Instance = new UnknownTypeUdi();
public override bool IsRoot
{
get { return false; }
}
}
}
}
+46 -43
View File
@@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Models;
namespace Umbraco.Core
{
public static partial class Constants
{
/// <summary>
@@ -13,81 +13,95 @@ namespace Umbraco.Core
/// but entity types are strings and so can be extended beyond what is defined here.</remarks>
public static class UdiEntityType
{
[UdiType(UdiType.Unknown)]
// note: const fields in this class MUST be consistent with what GetTypes returns
// this is validated by UdiTests.ValidateUdiEntityType
// also, this is used exclusively in Udi static ctor, only once, so there is no
// need to keep it around in a field nor to make it readonly
internal static Dictionary<string, UdiType> GetTypes()
{
return new Dictionary<string,UdiType>
{
{ Unknown, UdiType.Unknown },
{ AnyGuid, UdiType.GuidUdi },
{ Document, UdiType.GuidUdi },
{ Media, UdiType.GuidUdi },
{ Member, UdiType.GuidUdi },
{ DictionaryItem, UdiType.GuidUdi },
{ Macro, UdiType.GuidUdi },
{ Template, UdiType.GuidUdi },
{ DocumentType, UdiType.GuidUdi },
{ DocumentTypeContainer, UdiType.GuidUdi },
{ MediaType, UdiType.GuidUdi },
{ MediaTypeContainer, UdiType.GuidUdi },
{ DataType, UdiType.GuidUdi },
{ DataTypeContainer, UdiType.GuidUdi },
{ MemberType, UdiType.GuidUdi },
{ MemberGroup, UdiType.GuidUdi },
{ RelationType, UdiType.GuidUdi },
{ FormsForm, UdiType.GuidUdi },
{ FormsPreValue, UdiType.GuidUdi },
{ FormsDataSource, UdiType.GuidUdi },
{ AnyString, UdiType.StringUdi},
{ Language, UdiType.StringUdi},
{ MacroScript, UdiType.StringUdi},
{ MediaFile, UdiType.StringUdi},
{ TemplateFile, UdiType.StringUdi},
{ Script, UdiType.StringUdi},
{ PartialView, UdiType.StringUdi},
{ PartialViewMacro, UdiType.StringUdi},
{ Stylesheet, UdiType.StringUdi},
{ UserControl, UdiType.StringUdi},
{ Xslt, UdiType.StringUdi},
};
}
public const string Unknown = "unknown";
// guid entity types
[UdiType(UdiType.GuidUdi)]
public const string AnyGuid = "any-guid"; // that one is for tests
[UdiType(UdiType.GuidUdi)]
public const string Document = "document";
[UdiType(UdiType.GuidUdi)]
public const string Media = "media";
[UdiType(UdiType.GuidUdi)]
public const string Member = "member";
[UdiType(UdiType.GuidUdi)]
public const string DictionaryItem = "dictionary-item";
[UdiType(UdiType.GuidUdi)]
public const string Macro = "macro";
[UdiType(UdiType.GuidUdi)]
public const string Template = "template";
[UdiType(UdiType.GuidUdi)]
public const string DocumentType = "document-type";
[UdiType(UdiType.GuidUdi)]
public const string DocumentTypeContainer = "document-type-container";
[UdiType(UdiType.GuidUdi)]
public const string MediaType = "media-type";
[UdiType(UdiType.GuidUdi)]
public const string MediaTypeContainer = "media-type-container";
[UdiType(UdiType.GuidUdi)]
public const string DataType = "data-type";
[UdiType(UdiType.GuidUdi)]
public const string DataTypeContainer = "data-type-container";
[UdiType(UdiType.GuidUdi)]
public const string MemberType = "member-type";
[UdiType(UdiType.GuidUdi)]
public const string MemberGroup = "member-group";
[UdiType(UdiType.GuidUdi)]
public const string RelationType = "relation-type";
// forms
[UdiType(UdiType.GuidUdi)]
public const string FormsForm = "forms-form";
[UdiType(UdiType.GuidUdi)]
public const string FormsPreValue = "forms-prevalue";
[UdiType(UdiType.GuidUdi)]
public const string FormsDataSource = "forms-datasource";
// string entity types
[UdiType(UdiType.StringUdi)]
public const string AnyString = "any-string"; // that one is for tests
[UdiType(UdiType.StringUdi)]
public const string Language = "language";
[UdiType(UdiType.StringUdi)]
public const string MacroScript = "macroscript";
[UdiType(UdiType.StringUdi)]
public const string MediaFile = "media-file";
[UdiType(UdiType.StringUdi)]
public const string TemplateFile = "template-file";
[UdiType(UdiType.StringUdi)]
public const string Script = "script";
[UdiType(UdiType.StringUdi)]
public const string Stylesheet = "stylesheet";
[UdiType(UdiType.StringUdi)]
public const string PartialView = "partial-view";
[UdiType(UdiType.StringUdi)]
public const string PartialViewMacro = "partial-view-macro";
[UdiType(UdiType.StringUdi)]
public const string UserControl = "usercontrol";
[UdiType(UdiType.StringUdi)]
public const string Xslt = "xslt";
public static string FromUmbracoObjectType(UmbracoObjectTypes umbracoObjectType)
@@ -179,16 +193,5 @@ namespace Umbraco.Core
string.Format("EntityType \"{0}\" does not have a matching UmbracoObjectType.", entityType));
}
}
[AttributeUsage(AttributeTargets.Field)]
internal class UdiTypeAttribute : Attribute
{
public UdiType UdiType { get; private set; }
public UdiTypeAttribute(UdiType udiType)
{
UdiType = udiType;
}
}
}
}
+2 -2
View File
@@ -62,8 +62,8 @@ namespace Umbraco.Core
{
Uri uri;
if (!Uri.IsWellFormedUriString(s, UriKind.Absolute)
|| !Uri.TryCreate(s, UriKind.Absolute, out uri))
if (Uri.IsWellFormedUriString(s, UriKind.Absolute) == false
|| Uri.TryCreate(s, UriKind.Absolute, out uri) == false)
{
//if (tryParse) return false;
throw new FormatException(string.Format("String \"{0}\" is not a valid udi range.", s));
+8 -3
View File
@@ -45,6 +45,9 @@
<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>
<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>
@@ -148,6 +151,7 @@
<Compile Include="Attempt{T}.cs" />
<Compile Include="Auditing\Audit.cs" />
<Compile Include="Auditing\AuditTypes.cs" />
<Compile Include="Auditing\IdentityAuditEventArgs.cs" />
<Compile Include="BindingRedirects.cs" />
<Compile Include="ByteArrayExtensions.cs" />
<Compile Include="CacheHelper.cs" />
@@ -342,6 +346,7 @@
<Compile Include="Events\SupersedeEventAttribute.cs" />
<Compile Include="Exceptions\ConnectionException.cs" />
<Compile Include="HashCodeHelper.cs" />
<Compile Include="HashGenerator.cs" />
<Compile Include="IHttpContextAccessor.cs" />
<Compile Include="Models\EntityBase\IDeletableEntity.cs" />
<Compile Include="Models\IUserControl.cs" />
@@ -495,6 +500,7 @@
<Compile Include="Persistence\Factories\PublicAccessEntryFactory.cs" />
<Compile Include="Persistence\Factories\TaskFactory.cs" />
<Compile Include="Persistence\Factories\TaskTypeFactory.cs" />
<Compile Include="Persistence\LocalDb.cs" />
<Compile Include="Persistence\LockedRepository.cs" />
<Compile Include="Persistence\LockingRepository.cs" />
<Compile Include="Persistence\Mappers\AccessMapper.cs" />
@@ -586,6 +592,7 @@
<Compile Include="Persistence\Repositories\MigrationEntryRepository.cs" />
<Compile Include="Persistence\Repositories\PublicAccessRepository.cs" />
<Compile Include="Persistence\Repositories\RedirectUrlRepository.cs" />
<Compile Include="Persistence\Repositories\SimilarNodeName.cs" />
<Compile Include="Persistence\Repositories\UserControlRepository.cs" />
<Compile Include="Persistence\Repositories\XsltFileRepository.cs" />
<Compile Include="Persistence\Repositories\TaskRepository.cs" />
@@ -638,6 +645,7 @@
<Compile Include="Security\EmailService.cs" />
<Compile Include="Security\OwinExtensions.cs" />
<Compile Include="SemVersionExtensions.cs" />
<Compile Include="Serialization\KnownTypeUdiJsonConverter.cs" />
<Compile Include="Serialization\NoTypeConverterJsonConverter.cs" />
<Compile Include="Serialization\StreamResultExtensions.cs" />
<Compile Include="Serialization\UdiJsonConverter.cs" />
@@ -764,7 +772,6 @@
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenTwoZero\AddMissingForeignKeyForContentType.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenTwoZero\AlterDataTypePreValueTable.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenOneZero\AssignMissingPrimaryForMySqlKeys.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenTwoZero\AddIndexToUmbracoNodeTable.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenTwoZero\RemoveCmsDocumentAliasColumn.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSeven\AssignMissingKeysAndIndexes.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSixTwoZero\AssignMissingPrimaryForMySqlKeys.cs" />
@@ -1188,7 +1195,6 @@
<Compile Include="Persistence\Repositories\RepositoryBase.cs" />
<Compile Include="Persistence\Repositories\ScriptRepository.cs" />
<Compile Include="Persistence\Repositories\ServerRegistrationRepository.cs" />
<Compile Include="Persistence\Repositories\SimilarNodeNameComparer.cs" />
<Compile Include="Persistence\Repositories\StylesheetRepository.cs" />
<Compile Include="Persistence\Repositories\TemplateRepository.cs" />
<Compile Include="Persistence\Repositories\UserRepository.cs" />
@@ -1581,7 +1587,6 @@
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
<Import Project="..\packages\AutoMapper.3.3.1\tools\AutoMapper.targets" Condition="Exists('..\packages\AutoMapper.3.3.1\tools\AutoMapper.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
@@ -60,7 +60,7 @@ namespace Umbraco.Core.Xml
return -1;
});
const string rootXpath = "descendant::*[@id={0}]";
const string rootXpath = "id({0})";
//parseable items:
var vars = new Dictionary<string, Func<string, string>>();

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