Compare commits
397 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6ec90a3acd | |||
| 3556043b55 | |||
| 054ac78970 | |||
| b3cc5f9183 | |||
| 2b3397120f | |||
| ce99ec6d31 | |||
| 350f5c88c7 | |||
| 928c0dc535 | |||
| cc7a28db65 | |||
| 065e764957 | |||
| 637abc384b | |||
| 8959b81f6a | |||
| 32bb8ac5a4 | |||
| 016caba35f | |||
| d522dd7a31 | |||
| 95f632e1ea | |||
| e72ec171f7 | |||
| 2538e2e9c9 | |||
| b1ceeb23b3 | |||
| 579c572dd8 | |||
| bbf4c18825 | |||
| 9a94ac5317 | |||
| 0b904e82b5 | |||
| 51fec0c7d3 | |||
| 87ace12a4f | |||
| a1fd73cdb0 | |||
| 403502ef1b | |||
| c1769aa299 | |||
| 143d127fb9 | |||
| 01cf2e240a | |||
| 49689d5057 | |||
| f722d7731d | |||
| ac42f335c2 | |||
| f0eefbd5af | |||
| a8b660bfbf | |||
| dc7b5bae9b | |||
| 30b391f3fb | |||
| ff3b7963b1 | |||
| 84b380028c | |||
| f0b5c831f1 | |||
| d3232a09fb | |||
| 7cbaecc53a | |||
| e84a963d66 | |||
| 88d61a4d37 | |||
| a36184a261 | |||
| 75f4535157 | |||
| 9c50e1c48d | |||
| 78bf75615f | |||
| 9411a22a0d | |||
| 51649879cd | |||
| 461ce64feb | |||
| ab050eb7ac | |||
| c5a55b17e6 | |||
| fd577afe2c | |||
| f46ef0b006 | |||
| dc937ec942 | |||
| cefd9e7323 | |||
| 315690e758 | |||
| fb8c519f71 | |||
| 5df4d3509d | |||
| 4bd263e7bc | |||
| 916bc6badb | |||
| 2461924a89 | |||
| 581f15b172 | |||
| 33d7598d3c | |||
| 476773fce5 | |||
| 953355cddd | |||
| d2d43255aa | |||
| b39e218d51 | |||
| 4b581a2182 | |||
| 581eac5369 | |||
| 33cf983cae | |||
| 3ce6eb09aa | |||
| 963015cf4a | |||
| cc35af4eab | |||
| 349d89e4d7 | |||
| 1b3fc9fb21 | |||
| 2e98651d62 | |||
| 704e981284 | |||
| eda7f1e527 | |||
| 018a066f86 | |||
| e33a28ac19 | |||
| 0ef57d819c | |||
| c321d6dd44 | |||
| 5d74e61bcf | |||
| 8b39f495e5 | |||
| d468e346f1 | |||
| 2a10eed059 | |||
| 4ec04c779a | |||
| 26034fa33f | |||
| 97ddbdb1f0 | |||
| 731c51bfc7 | |||
| 78cb085e00 | |||
| f2ea4596d7 | |||
| f9d5de7af4 | |||
| 04c6fd0947 | |||
| 8bcf35734d | |||
| 4de7da2ac1 | |||
| 67d12ae2ca | |||
| e8ba069051 | |||
| a3457d2498 | |||
| 388d660e11 | |||
| b6dbc245e2 | |||
| 3efba6b576 | |||
| 54407a66a4 | |||
| bca46f11ad | |||
| a86b8f786d | |||
| ac3eaf43cb | |||
| f6d4170010 | |||
| 2b0ea45da9 | |||
| 07d05c9e0d | |||
| 26a5682a14 | |||
| f3fc2bcfb8 | |||
| c8d49ed5a7 | |||
| 9c2b0a3ef6 | |||
| 9355a81716 | |||
| 87575e9067 | |||
| a5b31e94b6 | |||
| f9e094a00a | |||
| 053175a5a6 | |||
| ce7488a924 | |||
| f442599633 | |||
| 37b6b42570 | |||
| f1149fb826 | |||
| 3f8f96e8c3 | |||
| a75181bd7f | |||
| 00b469b61b | |||
| 83f08538c2 | |||
| ef91e152b4 | |||
| add0685d3f | |||
| 27fc3a9bb3 | |||
| ac391f3bcd | |||
| 0700e37830 | |||
| 5f4985b4eb | |||
| d179792cc3 | |||
| 4cb7b29f60 | |||
| fb2f9cea64 | |||
| 394f21f4be | |||
| 32cd9d44cb | |||
| 237b7176bf | |||
| 1fa2c16668 | |||
| 1c5e2b5f73 | |||
| 30af8cb928 | |||
| 65b44137d4 | |||
| 41eeb38c5f | |||
| 4356d3f94a | |||
| ba072aec7c | |||
| f1d3b1a6d3 | |||
| 45f9919e57 | |||
| 857b7cc608 | |||
| c23160f915 | |||
| 3e3a180fe4 | |||
| c55cf75ada | |||
| 23950c0cac | |||
| 082e399293 | |||
| c4ab25f1ac | |||
| 3f55abaf59 | |||
| 001e15f83c | |||
| 34e3257ddb | |||
| 2b64cfb4b9 | |||
| a499744084 | |||
| c72ecda18c | |||
| 3423d8b2cc | |||
| 37c4fc5f74 | |||
| 406a206b03 | |||
| 55e0af118a | |||
| 718ed19fb9 | |||
| 32999b7e19 | |||
| ce63251206 | |||
| 7b641f4771 | |||
| 708d060c3b | |||
| e254dddbd4 | |||
| 87c2c5455c | |||
| 8544aa685b | |||
| 09c23e93c2 | |||
| 9946d36d00 | |||
| 7012633fdd | |||
| 6e348a3a1a | |||
| 733c920867 | |||
| 35fc997d9f | |||
| 921e8a2529 | |||
| 22bd6cd989 | |||
| 0713f12ba6 | |||
| dad4eafebd | |||
| 96c9ecbdd7 | |||
| e7e04de22e | |||
| 7003ef8d26 | |||
| 49a48bbfce | |||
| 663d9fcfe8 | |||
| d6f8b878d2 | |||
| 598944d3d8 | |||
| 3caecf5161 | |||
| b02b2a61f0 | |||
| 60f7211a80 | |||
| 56dc10a4f7 | |||
| dbda6689b6 | |||
| 8d6c8ef282 | |||
| df8c75438a | |||
| 2d53da55a7 | |||
| 18d0fec014 | |||
| 851d587d1f | |||
| 6a3f94d0e1 | |||
| b1358936b4 | |||
| b2798e38f7 | |||
| a603fd0c46 | |||
| 4b5c49907c | |||
| 1578bc8cc7 | |||
| e7c231c5a8 | |||
| a9cda9d651 | |||
| ad19eb8dff | |||
| e511554a42 | |||
| 84fd5dc2ee | |||
| 344b52d10f | |||
| 2121b7c8a0 | |||
| 895faf41b1 | |||
| 9a06c3a5ff | |||
| 44102df36f | |||
| 207d6b53b5 | |||
| 972d5f8296 | |||
| 617a91c57e | |||
| cb0e5414b9 | |||
| 22cef52261 | |||
| 41e419a49e | |||
| efbe97a01d | |||
| 0babb7f28a | |||
| 5ba801386c | |||
| 73937172dc | |||
| 8c1996a7a1 | |||
| 039ab8f1d2 | |||
| 8c998fd4e6 | |||
| 88f332f5c7 | |||
| 06a87bc341 | |||
| b10e646ade | |||
| 110a3b2a12 | |||
| 1bda9f32c1 | |||
| ce8390768e | |||
| 7ccfc79080 | |||
| ed850629da | |||
| 1307cb1173 | |||
| a1b9842215 | |||
| d300bf8d6d | |||
| 70231fcee2 | |||
| 78e66ce25f | |||
| dcc0374464 | |||
| a95afa610f | |||
| 13e2112c3c | |||
| cf10f5f0ee | |||
| ca00246236 | |||
| d71b8a139f | |||
| 6bf0d6e175 | |||
| aa5d7fc9fc | |||
| 5cbe2b6ce0 | |||
| 7a0c6ab975 | |||
| 68fd74897f | |||
| 047a41c05e | |||
| 49d5005453 | |||
| f2db0fe80e | |||
| 9917fb20ca | |||
| 0089f79bd7 | |||
| c468e4736e | |||
| 84f684ed31 | |||
| e71e9d7efb | |||
| 93cd03da28 | |||
| 1dade7bfd2 | |||
| c3b1aa36b8 | |||
| 9cc466775d | |||
| d0a91bf98b | |||
| 085c06e043 | |||
| 1e45f85f7c | |||
| a0785f2e0d | |||
| 583302faa2 | |||
| 4404ce94c5 | |||
| 86bc701511 | |||
| 54f86c06f0 | |||
| a86902bd91 | |||
| 7bdf76ffa7 | |||
| 528656a337 | |||
| 5ef46ad8df | |||
| 168dc0e5da | |||
| 5a1fc91f3e | |||
| 5e6b5a5cc1 | |||
| 7213e3a6a0 | |||
| 217ca62ea0 | |||
| d216d5755c | |||
| 8db748b738 | |||
| 300c9aa258 | |||
| f7097d571c | |||
| b151c39c3b | |||
| bb01eca7b7 | |||
| 542406c402 | |||
| c44b903261 | |||
| c68d7a33ca | |||
| 2773102441 | |||
| ef0d6eff2c | |||
| 1efad78214 | |||
| 46b2d8a4aa | |||
| 7a5cb7058b | |||
| 13f76d2af9 | |||
| c1c2339827 | |||
| 0e273d1a08 | |||
| 125c378274 | |||
| ca80a0cddc | |||
| 21707f9e08 | |||
| d8cfd8f12e | |||
| babcbd2f7a | |||
| ba82885b6d | |||
| 68ba0ca1e2 | |||
| b22e9d0911 | |||
| 6a59a90964 | |||
| 26a042f291 | |||
| 6c91d54372 | |||
| d4a2244190 | |||
| 14de3fee46 | |||
| 17d3548bbb | |||
| 9754c4511b | |||
| 442d359ab0 | |||
| bbed2fa8f6 | |||
| 3089a0ab3a | |||
| b3ac3a03f3 | |||
| bd0ca43b83 | |||
| f39531216c | |||
| 84de083d52 | |||
| 6ccb822cb5 | |||
| c94338d2b6 | |||
| 3ba001164f | |||
| a8c0367492 | |||
| 91646212fd | |||
| 4c9b2f552a | |||
| f24effdf0e | |||
| e9a4321ee8 | |||
| 3395445d7c | |||
| 9ea2fa9d8c | |||
| cddc6f3116 | |||
| 769f0d97d2 | |||
| f7d11bf936 | |||
| 0fc868c9ba | |||
| a3f4275b30 | |||
| cfd3301965 | |||
| 48041c3163 | |||
| cc57cd6eb7 | |||
| 193eb259a6 | |||
| 6db18d83be | |||
| 872530296f | |||
| 71b5e5223f | |||
| f3935b5007 | |||
| e35a748c92 | |||
| a3af1d478d | |||
| 7d8fdff6c1 | |||
| a813c0bfea | |||
| 4870554f47 | |||
| 547767fc0f | |||
| 74126134b1 | |||
| 6136118699 | |||
| 114b58c5ca | |||
| 3019d67922 | |||
| 97518c837f | |||
| 727250d2ab | |||
| 66e3a00e56 | |||
| aebea2da31 | |||
| 1634789641 | |||
| 631a9f92ea | |||
| 229a15be4f | |||
| 28d7a4a6dc | |||
| 21f003e705 | |||
| 0d80bf6a84 | |||
| e88f5c8fe2 | |||
| 7cb1a497e5 | |||
| b72c8ef953 | |||
| 04a09bd4d6 | |||
| 311fcfe328 | |||
| 33d09b14be | |||
| cdab89062e | |||
| 38571f2cdd | |||
| fcd90aa702 | |||
| ae17172126 | |||
| ecca480334 | |||
| 095a7c0aa7 | |||
| 169c6b6372 | |||
| 8ab2f7c257 | |||
| 51215f11af | |||
| 3df103eae2 | |||
| 1757e7b4c8 | |||
| a68eb91c14 | |||
| 7c9476ca41 | |||
| 5cee5a27c9 | |||
| 1e34570a1f | |||
| 7e930330e2 | |||
| cc3355b1f9 | |||
| ee9deacf92 | |||
| 4e9a8623f0 | |||
| 904265cdf5 | |||
| 4a191f0711 | |||
| 6be9a25d99 | |||
| 89cbe53b63 | |||
| 3ce3a6f6ea | |||
| b03e9f8fd5 | |||
| a58bf507ed |
+6
-11
@@ -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/
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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%
|
||||
|
||||
@@ -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
|
||||
-207
@@ -1,207 +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 vswhere.exe for VS2017+
|
||||
IF NOT EXIST "%toolsFolder%vswhere.exe" (
|
||||
ECHO vswhere not found - fetching now
|
||||
"%nuGetExecutable%" install vswhere -OutputDirectory tools -Verbosity quiet
|
||||
)
|
||||
|
||||
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
|
||||
@@ -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 "(.+)?""
|
||||
ReplacementText="CurrentComment { get { return "$(BUILD_COMMENT)""/>
|
||||
|
||||
<FileUpdate Files="..\src\Umbraco.Core\Configuration\UmbracoVersion.cs"
|
||||
Condition="'$(BUILD_NIGHTLY)'!=''"
|
||||
Regex="CurrentComment { get { return "(.+)?""
|
||||
ReplacementText="CurrentComment { get { return "$(BUILD_NIGHTLY)""/>
|
||||
|
||||
<FileUpdate Files="..\src\Umbraco.Core\Configuration\UmbracoVersion.cs"
|
||||
Condition="'$(BUILD_COMMENT)'!='' AND '$(BUILD_NIGHTLY)'!=''"
|
||||
Regex="CurrentComment { get { return "(.+)?""
|
||||
ReplacementText="CurrentComment { get { return "$(BUILD_COMMENT)-$(BUILD_NIGHTLY)""/>
|
||||
|
||||
<!--This updates the AssemblyFileVersion for the solution to the umbraco version-->
|
||||
<FileUpdate
|
||||
Files="..\src\SolutionInfo.cs"
|
||||
Regex="AssemblyFileVersion\("(.+)?"\)"
|
||||
ReplacementText="AssemblyFileVersion("$(BUILD_RELEASE)")"/>
|
||||
|
||||
<!--This updates the AssemblyInformationalVersion for the solution to the umbraco version and comment-->
|
||||
<FileUpdate
|
||||
Condition="'$(BUILD_COMMENT)'!=''"
|
||||
Files="..\src\SolutionInfo.cs"
|
||||
Regex="AssemblyInformationalVersion\("(.+)?"\)"
|
||||
ReplacementText="AssemblyInformationalVersion("$(BUILD_RELEASE)-$(BUILD_COMMENT)")"/>
|
||||
<FileUpdate
|
||||
Condition="'$(BUILD_COMMENT)'==''"
|
||||
Files="..\src\SolutionInfo.cs"
|
||||
Regex="AssemblyInformationalVersion\("(.+)?"\)"
|
||||
ReplacementText="AssemblyInformationalVersion("$(BUILD_RELEASE)")"/>
|
||||
<FileUpdate
|
||||
Condition="'$(BUILD_NIGHTLY)'!=''"
|
||||
Files="..\src\SolutionInfo.cs"
|
||||
Regex="AssemblyInformationalVersion\("(.+)?"\)"
|
||||
ReplacementText="AssemblyInformationalVersion("$(BUILD_RELEASE)-$(BUILD_NIGHTLY)")"/>
|
||||
<FileUpdate
|
||||
Condition="'$(BUILD_COMMENT)'!='' AND '$(BUILD_NIGHTLY)'!=''"
|
||||
Files="..\src\SolutionInfo.cs"
|
||||
Regex="AssemblyInformationalVersion\("(.+)?"\)"
|
||||
ReplacementText="AssemblyInformationalVersion("$(BUILD_RELEASE)-$(BUILD_COMMENT)-$(BUILD_NIGHTLY)")"/>
|
||||
<FileUpdate
|
||||
Condition="'$(BUILD_COMMENT)'=='' AND '$(BUILD_NIGHTLY)'==''"
|
||||
Files="..\src\SolutionInfo.cs"
|
||||
Regex="AssemblyInformationalVersion\("(.+)?"\)"
|
||||
ReplacementText="AssemblyInformationalVersion("$(BUILD_RELEASE)")"/>
|
||||
|
||||
<!--This updates the copyright year-->
|
||||
<FileUpdate
|
||||
Files="..\src\SolutionInfo.cs"
|
||||
Regex="AssemblyCopyright\("Copyright © Umbraco (\d{4})"\)"
|
||||
ReplacementText="AssemblyCopyright("Copyright © Umbraco $([System.DateTime]::Now.ToString(`yyyy`))")"/>
|
||||
|
||||
<XmlPoke XmlInputPath=".\NuSpecs\build\UmbracoCms.props"
|
||||
Namespaces="<Namespace Prefix='x' Uri='http://schemas.microsoft.com/developer/msbuild/2003' />"
|
||||
Query="//x:UmbracoVersion"
|
||||
Value="$(NewVersion)" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,68 +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
|
||||
|
||||
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%')"
|
||||
)
|
||||
|
||||
:: 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
|
||||
)
|
||||
|
||||
:: 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"
|
||||
|
||||
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 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%"
|
||||
@@ -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
|
||||
@@ -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,111 @@
|
||||
#
|
||||
|
||||
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
|
||||
# get a temp clean node env (will restore)
|
||||
Sandbox-Node $uenv
|
||||
|
||||
Write-Host "Executing gulp docs"
|
||||
|
||||
push-location "$($uenv.SolutionRoot)\src\Umbraco.Web.UI.Client"
|
||||
write "node version is:" > $tmp\belle-docs.log
|
||||
&node -v >> $tmp\belle-docs.log 2>&1
|
||||
write "npm version is:" >> $tmp\belle-docs.log 2>&1
|
||||
&npm -v >> $tmp\belle-docs.log 2>&1
|
||||
write "executing npm install" >> $tmp\belle-docs.log 2>&1
|
||||
&npm install >> $tmp\belle-docs.log 2>&1
|
||||
write "executing bower install" >> $tmp\belle-docs.log 2>&1
|
||||
&npm install -g bower >> $tmp\belle-docs.log 2>&1
|
||||
write "installing gulp" >> $tmp\belle-docs.log 2>&1
|
||||
&npm install -g gulp >> $tmp\belle-docs.log 2>&1
|
||||
write "installing gulp-cli" >> $tmp\belle-docs.log 2>&1
|
||||
&npm install -g gulp-cli --quiet >> $tmp\belle-docs.log 2>&1
|
||||
write "building docs using gulp" >> $tmp\belle-docs.log 2>&1
|
||||
&gulp docs >> $tmp\belle-docs.log 2>&1
|
||||
pop-location
|
||||
|
||||
Write-Host "Completed gulp docs build"
|
||||
|
||||
# fixme - should we filter the log to find errors?
|
||||
#get-content .\build.tmp\belle-docs.log | %{ if ($_ -match "build") { write $_}}
|
||||
|
||||
# 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,181 @@
|
||||
#
|
||||
# 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
|
||||
$file = ls -path "$dir" -name 7za.exe -recurse | select -first 1 #A select is because there is tools\7za.exe & tools\x64\7za.exe
|
||||
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,611 @@
|
||||
|
||||
# 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"
|
||||
Remove-Directory "$out"
|
||||
}
|
||||
|
||||
if (-not (Test-Path "$tmp"))
|
||||
{
|
||||
mkdir "$tmp" > $null
|
||||
}
|
||||
if (-not (Test-Path "$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
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -34,9 +34,9 @@
|
||||
<dependency id="ClientDependency-Mvc5" version="[1.8.0.0, 2.0.0)" />
|
||||
<dependency id="AutoMapper" version="[3.3.1, 4.0.0)" />
|
||||
<dependency id="Newtonsoft.Json" version="[10.0.2, 11.0.0)" />
|
||||
<dependency id="Examine" version="[0.1.83, 1.0.0)" />
|
||||
<dependency id="ImageProcessor" version="[2.5.3, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[4.8.3, 5.0.0)" />
|
||||
<dependency id="Examine" version="[0.1.88, 1.0.0)" />
|
||||
<dependency id="ImageProcessor" version="[2.5.6, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[4.8.7, 5.0.0)" />
|
||||
<dependency id="semver" version="[1.1.2, 3.0.0)" />
|
||||
<!-- Markdown can not be updated due to: https://github.com/hey-red/markdownsharp/issues/71#issuecomment-233585487 -->
|
||||
<dependency id="Markdown" version="[1.14.7, 2.0.0)" />
|
||||
@@ -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>
|
||||
|
||||
@@ -17,24 +17,24 @@
|
||||
<dependencies>
|
||||
<dependency id="UmbracoCms.Core" version="[$version$]" />
|
||||
<dependency id="Newtonsoft.Json" version="[10.0.2, 11.0.0)" />
|
||||
<dependency id="Umbraco.ModelsBuilder" version="[3.0.7, 4.0.0)" />
|
||||
<dependency id="Umbraco.ModelsBuilder" version="[3.0.8, 4.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.2.1, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web.Config" version="[2.3.0, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web.Config" version="[2.3.1, 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" />
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="InsertIfMissing" />
|
||||
|
||||
<add application="settings" alias="scripts" title="Scripts" type="umbraco.loadScripts, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="4"
|
||||
<add application="settings" alias="scripts" title="Scripts" type="Umbraco.Web.Trees.ScriptTreeController, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="4"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
<add application="settings" alias="dictionary" title="Dictionary" type="umbraco.loadDictionary, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="6"
|
||||
|
||||
Binary file not shown.
@@ -1,2 +0,0 @@
|
||||
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
|
||||
7.6.5
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<solution>
|
||||
<add key="disableSourceControlIntegration" value="true" />
|
||||
</solution>
|
||||
</configuration>
|
||||
Binary file not shown.
@@ -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>
|
||||
@@ -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
@@ -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.12")]
|
||||
[assembly: AssemblyInformationalVersion("7.6.12")]
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -518,24 +518,41 @@ namespace Umbraco.Core.Configuration
|
||||
|
||||
internal static bool ContentCacheXmlStoredInCodeGen
|
||||
{
|
||||
get { return ContentCacheXmlStorageLocation == ContentXmlStorage.AspNetTemp; }
|
||||
get { return LocalTempStorageLocation == LocalTempStorage.AspNetTemp; }
|
||||
}
|
||||
|
||||
internal static ContentXmlStorage ContentCacheXmlStorageLocation
|
||||
|
||||
/// <summary>
|
||||
/// This is the location type to store temporary files such as cache files or other localized files for a given machine
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Currently used for the xml cache file and the plugin cache files
|
||||
/// </remarks>
|
||||
internal static LocalTempStorage LocalTempStorageLocation
|
||||
{
|
||||
get
|
||||
{
|
||||
{
|
||||
//there's a bunch of backwards compat config checks here....
|
||||
|
||||
//This is the current one
|
||||
if (ConfigurationManager.AppSettings.ContainsKey("umbracoLocalTempStorage"))
|
||||
{
|
||||
return Enum<LocalTempStorage>.Parse(ConfigurationManager.AppSettings["umbracoLocalTempStorage"]);
|
||||
}
|
||||
|
||||
//This one is old
|
||||
if (ConfigurationManager.AppSettings.ContainsKey("umbracoContentXMLStorage"))
|
||||
{
|
||||
return Enum<ContentXmlStorage>.Parse(ConfigurationManager.AppSettings["umbracoContentXMLStorage"]);
|
||||
}
|
||||
return Enum<LocalTempStorage>.Parse(ConfigurationManager.AppSettings["umbracoContentXMLStorage"]);
|
||||
}
|
||||
|
||||
//This one is older
|
||||
if (ConfigurationManager.AppSettings.ContainsKey("umbracoContentXMLUseLocalTemp"))
|
||||
{
|
||||
return bool.Parse(ConfigurationManager.AppSettings["umbracoContentXMLUseLocalTemp"])
|
||||
? ContentXmlStorage.AspNetTemp
|
||||
: ContentXmlStorage.Default;
|
||||
? LocalTempStorage.AspNetTemp
|
||||
: LocalTempStorage.Default;
|
||||
}
|
||||
return ContentXmlStorage.Default;
|
||||
return LocalTempStorage.Default;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
namespace Umbraco.Core.Configuration
|
||||
{
|
||||
internal enum ContentXmlStorage
|
||||
internal enum LocalTempStorage
|
||||
{
|
||||
Default,
|
||||
AspNetTemp,
|
||||
@@ -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.12");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current version of Umbraco.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,7 +99,8 @@ namespace Umbraco.Core.Events
|
||||
/// </summary>
|
||||
public IEnumerable<TEntity> DeletedEntities
|
||||
{
|
||||
get { return EventObject; }
|
||||
get { return EventObject; }
|
||||
internal set { EventObject = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,11 +73,11 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (GlobalSettings.ContentCacheXmlStorageLocation)
|
||||
switch (GlobalSettings.LocalTempStorageLocation)
|
||||
{
|
||||
case ContentXmlStorage.AspNetTemp:
|
||||
case LocalTempStorage.AspNetTemp:
|
||||
return Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco.config");
|
||||
case ContentXmlStorage.EnvironmentTemp:
|
||||
case LocalTempStorage.EnvironmentTemp:
|
||||
var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1();
|
||||
var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoXml",
|
||||
//include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back
|
||||
@@ -85,7 +85,7 @@ namespace Umbraco.Core.IO
|
||||
// utilizing an old path
|
||||
appDomainHash);
|
||||
return Path.Combine(cachePath, "umbraco.config");
|
||||
case ContentXmlStorage.Default:
|
||||
case LocalTempStorage.Default:
|
||||
return IOHelper.ReturnPath("umbracoContentXML", "~/App_Data/umbraco.config");
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -278,10 +277,10 @@ namespace Umbraco.Core.Models
|
||||
if (Properties.Contains(propertyTypeAlias) == false)
|
||||
{
|
||||
return default(TPassType);
|
||||
}
|
||||
}
|
||||
|
||||
var convertAttempt = Properties[propertyTypeAlias].Value.TryConvertTo<TPassType>();
|
||||
return convertAttempt.Success ? convertAttempt.Result : default(TPassType);
|
||||
return convertAttempt.Success ? convertAttempt.Result : default(TPassType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,972 @@
|
||||
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;
|
||||
private string _exe;
|
||||
|
||||
#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;
|
||||
_exe = null;
|
||||
|
||||
var programFiles = Environment.GetEnvironmentVariable("ProgramFiles");
|
||||
|
||||
// MS SQL Server installs in e.g. "C:\Program Files\Microsoft SQL Server", so
|
||||
// we want to detect it in "%ProgramFiles%\Microsoft SQL Server" - however, if
|
||||
// Umbraco runs as a 32bits process (e.g. IISExpress configured as 32bits)
|
||||
// on a 64bits system, %ProgramFiles% will point to "C:\Program Files (x86)"
|
||||
// and SQL Server cannot be found. But then, %ProgramW6432% will point to
|
||||
// the original "C:\Program Files". Using it to fix the path.
|
||||
// see also: MSDN doc for WOW64 implementation
|
||||
//
|
||||
var programW6432 = Environment.GetEnvironmentVariable("ProgramW6432");
|
||||
if (string.IsNullOrWhiteSpace(programW6432) == false && programW6432 != programFiles)
|
||||
programFiles = programW6432;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(programFiles)) return;
|
||||
|
||||
// detect 14, 13, 12, 11
|
||||
for (var i = 14; i > 10; i--)
|
||||
{
|
||||
var exe = Path.Combine(programFiles, string.Format(@"Microsoft SQL Server\{0}0\Tools\Binn\SqlLocalDB.exe", i));
|
||||
if (File.Exists(exe) == false) continue;
|
||||
_version = i;
|
||||
_exe = exe;
|
||||
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)
|
||||
{
|
||||
if (_exe == null) // should never happen - we should not execute if not available
|
||||
{
|
||||
output = string.Empty;
|
||||
error = "SqlLocalDB.exe not found";
|
||||
return -1;
|
||||
}
|
||||
|
||||
var p = new Process
|
||||
{
|
||||
StartInfo =
|
||||
{
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
FileName = _exe,
|
||||
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
|
||||
}
|
||||
}
|
||||
-46
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,7 +133,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
.InnerJoin<ContentDto>(SqlSyntax)
|
||||
.On<ContentVersionDto, ContentDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
|
||||
.InnerJoin<NodeDto>(SqlSyntax)
|
||||
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId);
|
||||
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
|
||||
.InnerJoin<ContentTypeDto>()
|
||||
.On<ContentTypeDto, ContentDto>(left => left.NodeId, right => right.ContentTypeId);
|
||||
//TODO: IF we want to enable querying on content type information this will need to be joined
|
||||
//.InnerJoin<ContentTypeDto>(SqlSyntax)
|
||||
//.On<ContentDto, ContentTypeDto>(SqlSyntax, left => left.ContentTypeId, right => right.NodeId, SqlSyntax);
|
||||
@@ -435,7 +437,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);
|
||||
@@ -820,11 +839,21 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
|
||||
}
|
||||
|
||||
public int CountPublished()
|
||||
{
|
||||
var sql = GetBaseQuery(true).Where<NodeDto>(x => x.Trashed == false)
|
||||
.Where<DocumentDto>(x => x.Published == true);
|
||||
return Database.ExecuteScalar<int>(sql);
|
||||
public int CountPublished(string contentTypeAlias = null)
|
||||
{
|
||||
if (contentTypeAlias.IsNullOrWhiteSpace())
|
||||
{
|
||||
var sql = GetBaseQuery(true).Where<NodeDto>(x => x.Trashed == false)
|
||||
.Where<DocumentDto>(x => x.Published == true);
|
||||
return Database.ExecuteScalar<int>(sql);
|
||||
}
|
||||
else
|
||||
{
|
||||
var sql = GetBaseQuery(true).Where<NodeDto>(x => x.Trashed == false)
|
||||
.Where<DocumentDto>(x => x.Published == true)
|
||||
.Where<ContentTypeDto>(x => x.Alias == contentTypeAlias);
|
||||
return Database.ExecuteScalar<int>(sql);
|
||||
}
|
||||
}
|
||||
|
||||
public void ReplaceContentPermissions(EntityPermissionSet permissionSet)
|
||||
@@ -1122,31 +1151,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
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <remarks>
|
||||
/// We require this on the repo because the IQuery{IContent} cannot supply the 'newest' parameter
|
||||
/// </remarks>
|
||||
int CountPublished();
|
||||
int CountPublished(string contentTypeAlias = null);
|
||||
|
||||
/// <summary>
|
||||
/// Used to bulk update the permissions set for a content item. This will replace all permissions
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -705,8 +705,8 @@ ORDER BY contentNodeId, versionId, propertytypeid
|
||||
case "NAME":
|
||||
return "umbracoNode.text";
|
||||
case "PUBLISHED":
|
||||
case "OWNER":
|
||||
return "cmsDocument.published";
|
||||
case "OWNER":
|
||||
//TODO: This isn't going to work very nicely because it's going to order by ID, not by letter
|
||||
return "umbracoNode.nodeUser";
|
||||
// Members only
|
||||
|
||||
+167
-135
@@ -6,6 +6,7 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using System.Web.Compilation;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.IO;
|
||||
@@ -15,6 +16,7 @@ using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.Profiling;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using umbraco.interfaces;
|
||||
using Umbraco.Core.Configuration;
|
||||
using File = System.IO.File;
|
||||
|
||||
namespace Umbraco.Core
|
||||
@@ -39,13 +41,14 @@ namespace Umbraco.Core
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IRuntimeCacheProvider _runtimeCache;
|
||||
private readonly ProfilingLogger _logger;
|
||||
private readonly string _tempFolder;
|
||||
private readonly Lazy<string> _pluginListFilePath = new Lazy<string>(GetPluginListFilePath);
|
||||
private readonly Lazy<string> _pluginHashFilePath = new Lazy<string>(GetPluginHashFilePath);
|
||||
|
||||
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;
|
||||
|
||||
@@ -64,27 +67,21 @@ namespace Umbraco.Core
|
||||
|
||||
_serviceProvider = serviceProvider;
|
||||
_runtimeCache = runtimeCache;
|
||||
_logger = logger;
|
||||
|
||||
// the temp folder where the cache file lives
|
||||
_tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache");
|
||||
if (Directory.Exists(_tempFolder) == false)
|
||||
Directory.CreateDirectory(_tempFolder);
|
||||
|
||||
var pluginListFile = GetPluginListFilePath();
|
||||
|
||||
_logger = logger;
|
||||
|
||||
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)
|
||||
{
|
||||
// if the hash has changed, clear out the persisted list no matter what, this will force
|
||||
// rescanning of all plugin types including lazy ones.
|
||||
// http://issues.umbraco.org/issue/U4-4789
|
||||
File.Delete(pluginListFile);
|
||||
if (File.Exists(_pluginListFilePath.Value))
|
||||
File.Delete(_pluginListFilePath.Value);
|
||||
|
||||
WriteCachePluginsHash();
|
||||
}
|
||||
@@ -94,7 +91,8 @@ namespace Umbraco.Core
|
||||
// if the hash has changed, clear out the persisted list no matter what, this will force
|
||||
// rescanning of all plugin types including lazy ones.
|
||||
// http://issues.umbraco.org/issue/U4-4789
|
||||
File.Delete(pluginListFile);
|
||||
if (File.Exists(_pluginListFilePath.Value))
|
||||
File.Delete(_pluginListFilePath.Value);
|
||||
|
||||
// always set to true if we're not detecting (generally only for testing)
|
||||
RequiresRescanning = true;
|
||||
@@ -180,23 +178,19 @@ 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)
|
||||
return _cachedAssembliesHash;
|
||||
if (_cachedAssembliesHash != null)
|
||||
return _cachedAssembliesHash;
|
||||
|
||||
if (File.Exists(_pluginHashFilePath.Value) == false) return string.Empty;
|
||||
|
||||
var filePath = GetPluginHashFilePath();
|
||||
if (File.Exists(filePath) == false) return 0;
|
||||
var hash = File.ReadAllText(_pluginHashFilePath.Value, Encoding.UTF8);
|
||||
|
||||
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,15 +199,15 @@ 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>>
|
||||
{
|
||||
_currentAssembliesHash = GetFileHash(new List<Tuple<FileSystemInfo, bool>>
|
||||
{
|
||||
// the bin folder and everything in it
|
||||
new Tuple<FileSystemInfo, bool>(new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Bin)), false),
|
||||
// the app code folder and everything in it
|
||||
@@ -221,8 +215,8 @@ namespace Umbraco.Core
|
||||
// global.asax (the app domain also monitors this, if it changes will do a full restart)
|
||||
new Tuple<FileSystemInfo, bool>(new FileInfo(IOHelper.MapPath("~/global.asax")), false),
|
||||
// trees.config - use the contents to create the hash since this gets resaved on every app startup!
|
||||
new Tuple<FileSystemInfo, bool>(new FileInfo(IOHelper.MapPath(SystemDirectories.Config + "/trees.config")), true)
|
||||
}, _logger);
|
||||
new Tuple<FileSystemInfo, bool>(new FileInfo(IOHelper.MapPath(SystemDirectories.Config + "/trees.config")), true)
|
||||
}, _logger);
|
||||
|
||||
return _currentAssembliesHash;
|
||||
}
|
||||
@@ -232,9 +226,8 @@ namespace Umbraco.Core
|
||||
/// Writes the assembly hash file.
|
||||
/// </summary>
|
||||
private void WriteCachePluginsHash()
|
||||
{
|
||||
var filePath = GetPluginHashFilePath();
|
||||
File.WriteAllText(filePath, CurrentAssembliesHash.ToString(), Encoding.UTF8);
|
||||
{
|
||||
File.WriteAllText(_pluginHashFilePath.Value, CurrentAssembliesHash, Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -245,41 +238,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 +295,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
|
||||
@@ -360,12 +343,11 @@ namespace Umbraco.Core
|
||||
{
|
||||
return ReadCache();
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filePath = GetPluginListFilePath();
|
||||
File.Delete(filePath);
|
||||
File.Delete(_pluginListFilePath.Value);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -378,13 +360,12 @@ namespace Umbraco.Core
|
||||
|
||||
internal Dictionary<Tuple<string, string>, IEnumerable<string>> ReadCache()
|
||||
{
|
||||
var cache = new Dictionary<Tuple<string, string>, IEnumerable<string>>();
|
||||
|
||||
var filePath = GetPluginListFilePath();
|
||||
if (File.Exists(filePath) == false)
|
||||
var cache = new Dictionary<Tuple<string, string>, IEnumerable<string>>();
|
||||
|
||||
if (File.Exists(_pluginListFilePath.Value) == false)
|
||||
return cache;
|
||||
|
||||
using (var stream = GetFileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, ListFileOpenReadTimeout))
|
||||
using (var stream = GetFileStream(_pluginListFilePath.Value, FileMode.Open, FileAccess.Read, FileShare.Read, ListFileOpenReadTimeout))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
while (true)
|
||||
@@ -427,34 +408,86 @@ namespace Umbraco.Core
|
||||
/// <remarks>Generally only used for resetting cache, for example during the install process.</remarks>
|
||||
public void ClearPluginCache()
|
||||
{
|
||||
var path = GetPluginListFilePath();
|
||||
if (File.Exists(path))
|
||||
File.Delete(path);
|
||||
|
||||
path = GetPluginHashFilePath();
|
||||
if (File.Exists(path))
|
||||
File.Delete(path);
|
||||
if (File.Exists(_pluginListFilePath.Value))
|
||||
File.Delete(_pluginListFilePath.Value);
|
||||
|
||||
if (File.Exists(_pluginHashFilePath.Value))
|
||||
File.Delete(_pluginHashFilePath.Value);
|
||||
|
||||
_runtimeCache.ClearCacheItem(CacheKey);
|
||||
}
|
||||
|
||||
private string GetPluginListFilePath()
|
||||
private static string GetPluginListFilePath()
|
||||
{
|
||||
var filename = "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".list";
|
||||
return Path.Combine(_tempFolder, filename);
|
||||
string pluginListFilePath;
|
||||
switch (GlobalSettings.LocalTempStorageLocation)
|
||||
{
|
||||
case LocalTempStorage.AspNetTemp:
|
||||
pluginListFilePath = Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco-plugins.list");
|
||||
break;
|
||||
case LocalTempStorage.EnvironmentTemp:
|
||||
var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1();
|
||||
var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData",
|
||||
//include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back
|
||||
// to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not
|
||||
// utilizing an old path
|
||||
appDomainHash);
|
||||
pluginListFilePath = Path.Combine(cachePath, "umbraco-plugins.list");
|
||||
break;
|
||||
case LocalTempStorage.Default:
|
||||
default:
|
||||
var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache");
|
||||
pluginListFilePath = Path.Combine(tempFolder, "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".list");
|
||||
break;
|
||||
}
|
||||
|
||||
//ensure the folder exists
|
||||
var folder = Path.GetDirectoryName(pluginListFilePath);
|
||||
if (folder == null)
|
||||
throw new InvalidOperationException("The folder could not be determined for the file " + pluginListFilePath);
|
||||
if (Directory.Exists(folder) == false)
|
||||
Directory.CreateDirectory(folder);
|
||||
|
||||
return pluginListFilePath;
|
||||
}
|
||||
|
||||
private string GetPluginHashFilePath()
|
||||
private static string GetPluginHashFilePath()
|
||||
{
|
||||
var filename = "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".hash";
|
||||
return Path.Combine(_tempFolder, filename);
|
||||
string pluginHashFilePath;
|
||||
switch (GlobalSettings.LocalTempStorageLocation)
|
||||
{
|
||||
case LocalTempStorage.AspNetTemp:
|
||||
pluginHashFilePath = Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco-plugins.hash");
|
||||
break;
|
||||
case LocalTempStorage.EnvironmentTemp:
|
||||
var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1();
|
||||
var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData",
|
||||
//include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back
|
||||
// to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not
|
||||
// utilizing an old path
|
||||
appDomainHash);
|
||||
pluginHashFilePath = Path.Combine(cachePath, "umbraco-plugins.hash");
|
||||
break;
|
||||
case LocalTempStorage.Default:
|
||||
default:
|
||||
var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache");
|
||||
pluginHashFilePath = Path.Combine(tempFolder, "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".hash");
|
||||
break;
|
||||
}
|
||||
|
||||
//ensure the folder exists
|
||||
var folder = Path.GetDirectoryName(pluginHashFilePath);
|
||||
if (folder == null)
|
||||
throw new InvalidOperationException("The folder could not be determined for the file " + pluginHashFilePath);
|
||||
if (Directory.Exists(folder) == false)
|
||||
Directory.CreateDirectory(folder);
|
||||
|
||||
return pluginHashFilePath;
|
||||
}
|
||||
|
||||
internal void WriteCache()
|
||||
{
|
||||
var filePath = GetPluginListFilePath();
|
||||
|
||||
using (var stream = GetFileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None, ListFileOpenWriteTimeout))
|
||||
using (var stream = GetFileStream(_pluginListFilePath.Value, FileMode.Create, FileAccess.Write, FileShare.None, ListFileOpenWriteTimeout))
|
||||
using (var writer = new StreamWriter(stream))
|
||||
{
|
||||
foreach (var typeList in _types.Values)
|
||||
@@ -470,8 +503,7 @@ namespace Umbraco.Core
|
||||
|
||||
internal void UpdateCache()
|
||||
{
|
||||
// note
|
||||
// at the moment we write the cache to disk every time we update it. ideally we defer the writing
|
||||
// TODO: at the moment we write the cache to disk every time we update it. ideally we defer the writing
|
||||
// since all the updates are going to happen in a row when Umbraco starts. that being said, the
|
||||
// file is small enough, so it is not a priority.
|
||||
WriteCache();
|
||||
@@ -484,13 +516,13 @@ namespace Umbraco.Core
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
{
|
||||
return new FileStream(path, fileMode, fileAccess, fileShare);
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (--attempts == 0)
|
||||
throw;
|
||||
throw;
|
||||
|
||||
LogHelper.Debug<PluginManager>(string.Format("Attempted to get filestream for file {0} failed, {1} attempts left, pausing for {2} milliseconds", path, attempts, pauseMilliseconds));
|
||||
Thread.Sleep(pauseMilliseconds);
|
||||
@@ -569,7 +601,7 @@ namespace Umbraco.Core
|
||||
if (cache == false || typeof(IDiscoverable).IsAssignableFrom(typeof(T)) == false)
|
||||
{
|
||||
return ResolveTypesInternal(
|
||||
typeof (T), null,
|
||||
typeof(T), null,
|
||||
() => TypeFinder.FindClassesOfType<T>(specificAssemblies ?? AssembliesToScan),
|
||||
cache);
|
||||
}
|
||||
@@ -578,14 +610,14 @@ namespace Umbraco.Core
|
||||
// filter the cached discovered types (and cache the result)
|
||||
|
||||
var discovered = ResolveTypesInternal(
|
||||
typeof (IDiscoverable), null,
|
||||
typeof(IDiscoverable), null,
|
||||
() => TypeFinder.FindClassesOfType<IDiscoverable>(AssembliesToScan),
|
||||
true);
|
||||
|
||||
return ResolveTypesInternal(
|
||||
typeof (T), null,
|
||||
typeof(T), null,
|
||||
() => discovered
|
||||
.Where(x => typeof (T).IsAssignableFrom(x)),
|
||||
.Where(x => typeof(T).IsAssignableFrom(x)),
|
||||
true);
|
||||
}
|
||||
|
||||
@@ -608,7 +640,7 @@ namespace Umbraco.Core
|
||||
if (cache == false || typeof(IDiscoverable).IsAssignableFrom(typeof(T)) == false)
|
||||
{
|
||||
return ResolveTypesInternal(
|
||||
typeof (T), typeof (TAttribute),
|
||||
typeof(T), typeof(TAttribute),
|
||||
() => TypeFinder.FindClassesOfTypeWithAttribute<T, TAttribute>(specificAssemblies ?? AssembliesToScan),
|
||||
cache);
|
||||
}
|
||||
@@ -617,12 +649,12 @@ namespace Umbraco.Core
|
||||
// filter the cached discovered types (and cache the result)
|
||||
|
||||
var discovered = ResolveTypesInternal(
|
||||
typeof (IDiscoverable), null,
|
||||
typeof(IDiscoverable), null,
|
||||
() => TypeFinder.FindClassesOfType<IDiscoverable>(AssembliesToScan),
|
||||
true);
|
||||
|
||||
return ResolveTypesInternal(
|
||||
typeof (T), typeof (TAttribute),
|
||||
typeof(T), typeof(TAttribute),
|
||||
() => discovered
|
||||
.Where(x => typeof(T).IsAssignableFrom(x))
|
||||
.Where(x => x.GetCustomAttributes<TAttribute>(false).Any()),
|
||||
@@ -644,7 +676,7 @@ namespace Umbraco.Core
|
||||
cache &= specificAssemblies == null;
|
||||
|
||||
return ResolveTypesInternal(
|
||||
typeof (object), typeof (TAttribute),
|
||||
typeof(object), typeof(TAttribute),
|
||||
() => TypeFinder.FindClassesWithAttribute<TAttribute>(specificAssemblies ?? AssembliesToScan),
|
||||
cache);
|
||||
}
|
||||
@@ -661,14 +693,14 @@ namespace Umbraco.Core
|
||||
|
||||
var name = ResolvedName(baseType, attributeType);
|
||||
|
||||
lock (_typesLock)
|
||||
using (_logger.TraceDuration<PluginManager>(
|
||||
"Resolving " + name,
|
||||
"Resolved " + name)) // cannot contain typesFound.Count as it's evaluated before the find
|
||||
{
|
||||
// resolve within a lock & timer
|
||||
return ResolveTypesInternalLocked(baseType, attributeType, finder, cache);
|
||||
}
|
||||
lock (_typesLock)
|
||||
using (_logger.TraceDuration<PluginManager>(
|
||||
"Resolving " + name,
|
||||
"Resolved " + name)) // cannot contain typesFound.Count as it's evaluated before the find
|
||||
{
|
||||
// resolve within a lock & timer
|
||||
return ResolveTypesInternalLocked(baseType, attributeType, finder, cache);
|
||||
}
|
||||
}
|
||||
|
||||
private static string ResolvedName(Type baseType, Type attributeType)
|
||||
@@ -700,7 +732,7 @@ namespace Umbraco.Core
|
||||
// else proceed,
|
||||
typeList = new TypeList(baseType, attributeType);
|
||||
|
||||
var scan = RequiresRescanning || File.Exists(GetPluginListFilePath()) == false;
|
||||
var scan = RequiresRescanning || File.Exists(_pluginListFilePath.Value) == false;
|
||||
|
||||
if (scan)
|
||||
{
|
||||
@@ -804,7 +836,7 @@ namespace Umbraco.Core
|
||||
|
||||
public TypeListKey(Type baseType, Type attributeType)
|
||||
{
|
||||
BaseType = baseType ?? typeof (object);
|
||||
BaseType = baseType ?? typeof(object);
|
||||
AttributeType = attributeType;
|
||||
}
|
||||
|
||||
@@ -822,7 +854,7 @@ namespace Umbraco.Core
|
||||
|
||||
var hash = 5381;
|
||||
hash = ((hash << 5) + hash) ^ BaseType.GetHashCode();
|
||||
hash = ((hash << 5) + hash) ^ (AttributeType ?? typeof (TypeListKey)).GetHashCode();
|
||||
hash = ((hash << 5) + hash) ^ (AttributeType ?? typeof(TypeListKey)).GetHashCode();
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
@@ -885,7 +917,7 @@ namespace Umbraco.Core
|
||||
{
|
||||
// look for IParameterEditor (fast, IDiscoverable) then filter
|
||||
|
||||
var propertyEditor = typeof (PropertyEditor);
|
||||
var propertyEditor = typeof(PropertyEditor);
|
||||
|
||||
return mgr.ResolveTypes<IParameterEditor>()
|
||||
.Where(x => propertyEditor.IsAssignableFrom(x) && x != propertyEditor);
|
||||
@@ -900,8 +932,8 @@ namespace Umbraco.Core
|
||||
/// </remarks>
|
||||
public static IEnumerable<Type> ResolveParameterEditors(this PluginManager mgr)
|
||||
{
|
||||
var propertyEditor = typeof (PropertyEditor);
|
||||
var parameterEditor = typeof (ParameterEditor);
|
||||
var propertyEditor = typeof(PropertyEditor);
|
||||
var parameterEditor = typeof(ParameterEditor);
|
||||
|
||||
return mgr.ResolveTypes<IParameterEditor>()
|
||||
.Where(x => x != propertyEditor && x != parameterEditor);
|
||||
@@ -983,4 +1015,4 @@ namespace Umbraco.Core
|
||||
return mgr.ResolveTypesWithAttribute<ISqlSyntaxProvider, SqlSyntaxProviderAttribute>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
+1
-1
@@ -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();
|
||||
|
||||
@@ -12,6 +12,7 @@ using Umbraco.Core.Models.Identity;
|
||||
|
||||
namespace Umbraco.Core.Security
|
||||
{
|
||||
//TODO: In v8 we need to change this to use an int? nullable TKey instead, see notes against overridden TwoFactorSignInAsync
|
||||
public class BackOfficeSignInManager : SignInManager<BackOfficeIdentityUser, int>
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
@@ -49,7 +50,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 +105,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 +130,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 +194,7 @@ namespace Umbraco.Core.Security
|
||||
AllowRefresh = true,
|
||||
IssuedUtc = nowUtc,
|
||||
ExpiresUtc = nowUtc.AddMinutes(GlobalSettings.TimeOutInMinutes)
|
||||
}, userIdentity, rememberBrowserIdentity);
|
||||
}, userIdentity, rememberBrowserIdentity);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -191,7 +209,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,
|
||||
@@ -231,5 +251,67 @@ namespace Umbraco.Core.Security
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Two factor verification step
|
||||
/// </summary>
|
||||
/// <param name="provider"></param>
|
||||
/// <param name="code"></param>
|
||||
/// <param name="isPersistent"></param>
|
||||
/// <param name="rememberBrowser"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This is implemented because we cannot override GetVerifiedUserIdAsync and instead we have to shadow it
|
||||
/// so due to this and because we are using an INT as the TKey and not an object, it can never be null. Adding to that
|
||||
/// the default(int) value returned by the base class is always a valid user (i.e. the admin) so we just have to duplicate
|
||||
/// all of this code to check for -1 instead.
|
||||
/// </remarks>
|
||||
public override async Task<SignInStatus> TwoFactorSignInAsync(string provider, string code, bool isPersistent, bool rememberBrowser)
|
||||
{
|
||||
var userId = await GetVerifiedUserIdAsync();
|
||||
if (userId == -1)
|
||||
{
|
||||
return SignInStatus.Failure;
|
||||
}
|
||||
var user = await UserManager.FindByIdAsync(userId);
|
||||
if (user == null)
|
||||
{
|
||||
return SignInStatus.Failure;
|
||||
}
|
||||
if (await UserManager.IsLockedOutAsync(user.Id))
|
||||
{
|
||||
return SignInStatus.LockedOut;
|
||||
}
|
||||
if (await UserManager.VerifyTwoFactorTokenAsync(user.Id, provider, code))
|
||||
{
|
||||
// When token is verified correctly, clear the access failed count used for lockout
|
||||
await UserManager.ResetAccessFailedCountAsync(user.Id);
|
||||
await SignInAsync(user, isPersistent, rememberBrowser);
|
||||
return SignInStatus.Success;
|
||||
}
|
||||
// If the token is incorrect, record the failure which also may cause the user to be locked out
|
||||
await UserManager.AccessFailedAsync(user.Id);
|
||||
return SignInStatus.Failure;
|
||||
}
|
||||
|
||||
/// <summary>Send a two factor code to a user</summary>
|
||||
/// <param name="provider"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This is implemented because we cannot override GetVerifiedUserIdAsync and instead we have to shadow it
|
||||
/// so due to this and because we are using an INT as the TKey and not an object, it can never be null. Adding to that
|
||||
/// the default(int) value returned by the base class is always a valid user (i.e. the admin) so we just have to duplicate
|
||||
/// all of this code to check for -1 instead.
|
||||
/// </remarks>
|
||||
public override async Task<bool> SendTwoFactorCodeAsync(string provider)
|
||||
{
|
||||
var userId = await GetVerifiedUserIdAsync();
|
||||
if (userId == -1)
|
||||
return false;
|
||||
|
||||
var token = await UserManager.GenerateTwoFactorTokenAsync(userId, provider);
|
||||
var identityResult = await UserManager.NotifyTwoFactorTokenAsync(userId, provider, token);
|
||||
return identityResult.Succeeded;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -37,11 +37,19 @@ namespace Umbraco.Core.Security
|
||||
var identity = Thread.CurrentPrincipal.GetUmbracoIdentity();
|
||||
if (identity != null)
|
||||
{
|
||||
var user = userService.GetByUsername(identity.Username);
|
||||
var userIsAdmin = user.IsAdmin();
|
||||
if (userIsAdmin)
|
||||
//get the user id from the identity
|
||||
var userId = 0;
|
||||
if(int.TryParse(identity.Id.ToString(), out userId))
|
||||
{
|
||||
canReset = true;
|
||||
var user = userService.GetUserById(userId);
|
||||
if (user != null)
|
||||
{
|
||||
var userIsAdmin = user.IsAdmin();
|
||||
if (userIsAdmin)
|
||||
{
|
||||
canReset = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -67,7 +65,7 @@ namespace Umbraco.Core.Services
|
||||
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
|
||||
{
|
||||
var repository = RepositoryFactory.CreateContentRepository(uow);
|
||||
return repository.CountPublished();
|
||||
return repository.CountPublished(contentTypeAlias);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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, "Saving"))
|
||||
{
|
||||
uow.Commit();
|
||||
return false;
|
||||
@@ -1774,7 +1829,100 @@ 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, "Saved");
|
||||
}
|
||||
|
||||
if (shouldBePublished.Any())
|
||||
{
|
||||
//TODO: This should not be an inner operation, but if we do this, it cannot raise events and cannot be cancellable!
|
||||
_publishingStrategy.PublishingFinalized(uow, shouldBePublished, false);
|
||||
}
|
||||
|
||||
Audit(uow, AuditType.Sort, "Sorting content performed by user", userId, 0);
|
||||
uow.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sorts a collection of <see cref="IContent"/> objects by updating the SortOrder according
|
||||
/// to the ordering of node Ids passed in.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Using this method will ensure that the Published-state is maintained upon sorting
|
||||
/// so the cache is updated accordingly - as needed.
|
||||
/// </remarks>
|
||||
/// <param name="ids"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="raiseEvents"></param>
|
||||
/// <returns>True if sorting succeeded, otherwise False</returns>
|
||||
public bool Sort(int[] ids, int userId = 0, bool raiseEvents = true)
|
||||
{
|
||||
var shouldBePublished = new List<IContent>();
|
||||
var shouldBeSaved = new List<IContent>();
|
||||
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var allContent = GetByIds(ids).ToDictionary(x => x.Id, x => x);
|
||||
var items = ids.Select(x => allContent[x]);
|
||||
|
||||
using (var uow = UowProvider.GetUnitOfWork())
|
||||
{
|
||||
var asArray = items.ToArray();
|
||||
var saveEventArgs = new SaveEventArgs<IContent>(asArray);
|
||||
if (raiseEvents && uow.Events.DispatchCancelable(Saving, this, saveEventArgs, "Saving"))
|
||||
{
|
||||
uow.Commit();
|
||||
return false;
|
||||
}
|
||||
|
||||
var repository = RepositoryFactory.CreateContentRepository(uow);
|
||||
|
||||
var i = 0;
|
||||
foreach (var content in asArray)
|
||||
{
|
||||
//If the current sort order equals that of the content
|
||||
//we don't need to update it, so just increment the sort order
|
||||
//and continue.
|
||||
if (content.SortOrder == i)
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
content.SortOrder = i;
|
||||
content.WriterId = userId;
|
||||
i++;
|
||||
|
||||
if (content.Published)
|
||||
{
|
||||
//TODO: This should not be an inner operation, but if we do this, it cannot raise events and cannot be cancellable!
|
||||
var published = _publishingStrategy.Publish(uow, content, userId).Success;
|
||||
shouldBePublished.Add(content);
|
||||
}
|
||||
else
|
||||
shouldBeSaved.Add(content);
|
||||
|
||||
repository.AddOrUpdate(content);
|
||||
//add or update a preview
|
||||
repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, _userService, c));
|
||||
}
|
||||
|
||||
foreach (var content in shouldBePublished)
|
||||
{
|
||||
//Create and Save ContentXml DTO
|
||||
repository.AddOrUpdateContentXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, _userService, c));
|
||||
}
|
||||
|
||||
if (raiseEvents)
|
||||
{
|
||||
saveEventArgs.CanCancel = false;
|
||||
uow.Events.Dispatch(Saved, this, saveEventArgs, "Saved");
|
||||
}
|
||||
|
||||
if (shouldBePublished.Any())
|
||||
{
|
||||
@@ -2138,7 +2286,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 +2342,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 +2384,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 +2414,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 +2427,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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -618,7 +618,21 @@ namespace Umbraco.Core.Services
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="raiseEvents"></param>
|
||||
/// <returns>True if sorting succeeded, otherwise False</returns>
|
||||
bool Sort(IEnumerable<IContent> items, int userId = 0, bool raiseEvents = true);
|
||||
bool Sort(IEnumerable<IContent> items, int userId = 0, bool raiseEvents = true);
|
||||
|
||||
/// <summary>
|
||||
/// Sorts a collection of <see cref="IContent"/> objects by updating the SortOrder according
|
||||
/// to the ordering of node Ids passed in.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Using this method will ensure that the Published-state is maintained upon sorting
|
||||
/// so the cache is updated accordingly - as needed.
|
||||
/// </remarks>
|
||||
/// <param name="ids"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="raiseEvents"></param>
|
||||
/// <returns>True if sorting succeeded, otherwise False</returns>
|
||||
bool Sort(int[] ids, int userId = 0, bool raiseEvents = true);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent of the current content as an <see cref="IContent"/> item.
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -38,19 +39,22 @@ namespace Umbraco.Core.Services
|
||||
int? val;
|
||||
using (var uow = _uowProvider.GetUnitOfWork())
|
||||
{
|
||||
val = uow.Database.ExecuteScalar<int?>("SELECT id FROM umbracoNode WHERE uniqueId=@id AND nodeObjectType=@nodeObjectType",
|
||||
new { id = key, nodeObjectType = GetNodeObjectTypeGuid(umbracoObjectType) });
|
||||
val = uow.Database.ExecuteScalar<int?>("SELECT id FROM umbracoNode WHERE uniqueId=@id AND (nodeObjectType=@type OR nodeObjectType=@reservation)",
|
||||
new { id = key, type = GetNodeObjectTypeGuid(umbracoObjectType), reservation = Constants.ObjectTypes.IdReservationGuid });
|
||||
uow.Commit();
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
@@ -88,19 +92,22 @@ namespace Umbraco.Core.Services
|
||||
Guid? val;
|
||||
using (var uow = _uowProvider.GetUnitOfWork())
|
||||
{
|
||||
val = uow.Database.ExecuteScalar<Guid?>("SELECT uniqueId FROM umbracoNode WHERE id=@id AND nodeObjectType=@nodeObjectType",
|
||||
new { id, nodeObjectType = GetNodeObjectTypeGuid(umbracoObjectType) });
|
||||
val = uow.Database.ExecuteScalar<Guid?>("SELECT uniqueId FROM umbracoNode WHERE id=@id AND (nodeObjectType=@type OR nodeObjectType=@reservation)",
|
||||
new { id, type = GetNodeObjectTypeGuid(umbracoObjectType), reservation = Constants.ObjectTypes.IdReservationGuid });
|
||||
uow.Commit();
|
||||
}
|
||||
|
||||
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>(id, umbracoObjectType);
|
||||
}
|
||||
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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user