Revert "Temp8 tinymce"

This commit is contained in:
Warren Buckley
2018-11-22 14:05:51 +00:00
committed by GitHub
parent 2a0748fc1e
commit 54a2aa00a7
6677 changed files with 646351 additions and 410535 deletions

View File

@@ -8,29 +8,9 @@ root = true
# Use 4 spaces as indentation
[*]
insert_final_newline = true
end_of_line = crlf
indent_style = space
indent_size = 4
# Trim trailing whitespace, limited support.
# https://github.com/editorconfig/editorconfig/wiki/Property-research:-Trim-trailing-spaces
trim_trailing_whitespace = true
[*.{cs,vb}]
dotnet_style_predefined_type_for_locals_parameters_members = true:error
dotnet_naming_rule.private_members_with_underscore.symbols = private_fields
dotnet_naming_rule.private_members_with_underscore.style = prefix_underscore
dotnet_naming_rule.private_members_with_underscore.severity = suggestion
dotnet_naming_symbols.private_fields.applicable_kinds = field
dotnet_naming_symbols.private_fields.applicable_accessibilities = private
dotnet_naming_style.prefix_underscore.capitalization = camel_case
dotnet_naming_style.prefix_underscore.required_prefix = _
# https://github.com/MicrosoftDocs/visualstudio-docs/blob/master/docs/ide/editorconfig-code-style-settings-reference.md
[*.cs]
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_var_elsewhere = true:suggestion
trim_trailing_whitespace = true

62
.github/BUILD.md vendored
View File

@@ -6,13 +6,19 @@ Umbraco Cms Build
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
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.
## PowerShell Quirks
There is a good chance that running `build.ps1` ends up in error, with messages such as
>The file ...\build.ps1 is not digitally signed. You cannot run this script on the current system. For more information about running scripts and setting execution policy, see about_Execution_Policies.
>The file ...\build\build.ps1 is not digitally signed. You cannot run this script on the current system. For more information about running scripts and setting execution policy, see about_Execution_Policies.
PowerShell has *Execution Policies* that may prevent the script from running. You can check the current policies with:
@@ -36,7 +42,7 @@ Alternatively, you can do it at machine level, from within an elevated PowerShel
And *then* the script should run. It *might* however still complain about executing scripts, with messages such as:
>Security warning - Run only scripts that you trust. While scripts from the internet can be useful, this script can potentially harm your computer. If you trust this script, use the Unblock-File cmdlet to allow the script to run without this warning message. Do you want to run ...\build.ps1?
>Security warning - Run only scripts that you trust. While scripts from the internet can be useful, this script can potentially harm your computer. If you trust this script, use the Unblock-File cmdlet to allow the script to run without this warning message. Do you want to run ...\build\build.ps1?
[D] Do not run [R] Run once [S] Suspend [?] Help (default is "D"):
This is usually caused by the scripts being *blocked*. And that usually happens when the source code has been downloaded as a Zip file. When Windows downloads Zip files, they are marked as *blocked* (technically, they have a Zone.Identifier alternate data stream, with a value of "3" to indicate that they were downloaded from the Internet). And when such a Zip file is un-zipped, each and every single file is also marked as blocked.
@@ -45,20 +51,30 @@ The best solution is to unblock the Zip file before un-zipping: right-click the
PS> Get-ChildItem -Recurse *.* | Unblock-File
## Git Quirks
## 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 Infrastructure
# Build
The Umbraco Build infrastructure relies on a PowerShell object. The object can be retrieved with:
The Umbraco Build solution relies on a PowerShell module. The module needs to be imported into PowerShell. From within Umbraco's repository root:
$ubuild = build/build.ps1 -get
build\build.ps1 -ModuleOnly
The object exposes various properties and methods that can be used to fine-grain build Umbraco. Some, but not all, of them are detailed below.
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
## Properties
The object exposes the following properties:
* `SolutionRoot`: the absolute path to the solution root
@@ -76,11 +92,11 @@ The Visual Studio object is `null` when Visual Studio has not been detected (eg
* `Minor`: Visual Studio minor version
* `MsBUild`: the absolute path to the MsBuild executable
## GetUmbracoVersion
## Get-UmbracoVersion
Gets an object representing the current Umbraco version. Example:
$v = $ubuild.GetUmbracoVersion()
$v = Get-UmbracoVersion
Write-Host $v.Semver
The object exposes the following properties:
@@ -90,24 +106,28 @@ The object exposes the following properties:
* `Comment`: the pre release part of the version (eg `alpha02`)
* `Build`: the build number part of the version (eg `1234`)
## SetUmbracoVersion
## Set-UmbracoVersion
Modifies Umbraco files with the new version.
>This entirely replaces the legacy `UmbracoVersion.txt` file. Do *not* edit version infos in files.
>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:
$ubuild.SetUmbracoVersion("7.6.33")
$ubuild.SetUmbracoVersion("7.6.33-alpha.2")
$ubuild.SetUmbracoVersion("7.6.33+1234")
$ubuild.SetUmbracoVersion("7.6.33-beta.5+5678")
Set-UmbracoVersion 7.6.33
Set-UmbracoVersion 7.6.33-alpha02
Set-UmbracoVersion 7.6.33+1234
Set-UmbracoVersion 7.6.33-beta05+5678
## Build
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:
$ubuild.Build()
Build-Umbraco
Some log files, such as MsBuild logs, are produced in `build.tmp` too. The `build` directory should remain clean during a build.
@@ -161,4 +181,6 @@ We should increment versions as soon as a version is released. Ie, as soon as `7
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

34
.github/CLEAR.md vendored
View File

@@ -1,34 +0,0 @@
Umbraco Cms Clear
--
----
Once the solution has been used to run a site, one may want to "reset" the solution in order to run a fresh new site again.
## Fast
At the very minimum, you want
git clean -Xdf src/Umbraco.Web.UI/App_Data
rm src/Umbraco.Web.UI/web.config
Then, a simple 'Rebuild All' in Visual Studio will recreate a fresh `web.config` but should be quite fast (since it does not really need to rebuild anything).
The `clean` Git command force (`-f`) removes (`-X`, note the capital X) all files and directories (`-d`) that are ignored by Git.
This will leave medias and views around, but in most cases, it will be enough.
## More
To perform a more complete clear, you will want to also delete the content of the media, views, masterpages, scripts... directories.
## Full
The following command will force remove all untracked files and directories, be they ignored by Git or not. Combined with `git reset` it can recreate a pristine working directory.
git clean -xdf .
## Docs
See
* git [clean](<https://git-scm.com/docs/git-clean>)
* git [reset](<https://git-scm.com/docs/git-reset>)

17
.github/README.md vendored
View File

@@ -1,17 +1,6 @@
_You are browsing the Umbraco v8 branch. Umbraco 8 is currently under development._
[![Build status](https://ci.appveyor.com/api/projects/status/6by6harxtxt0ocdx/branch/dev-v7?svg=true)](https://ci.appveyor.com/project/Umbraco/umbraco-cms-b2cri/branch/dev-v7)
_Looking for Umbraco version 7? [Click here](https://github.com/umbraco/Umbraco-CMS) to go to the v7 branch._
_Ready to try out Version 8? [See the quick start guide](V8_GETTING_STARTED.md)._
When is Umbraco 8 coming?
=========================
When it's ready. We're done with the major parts of the architecture work and are focusing on three seperate tracks to prepare Umbraco 8 for release:
1) Editor Track (_currently in progress_). Without editors, there's no market for Umbraco. So we want to make sure that Umbraco 8 is full of love for editors.
2) Partner Track. Without anyone implementing Umbraco, there's nothing for editors to update. So we want to make sure that Umbraco 8 is a joy to implement
3) Contributor Track. Without our fabulous ecosystem of both individual Umbracians and 3rd party ISVs, Umbraco wouldn't be as rich a platform as it is today. We want to make sure that it's easy, straight forward and as backwards-compatible as possible to create packages for Umbraco
Once a track is done, we start releasing previews where we ask people to test the features we believe are ready. While the testing is going on and we gather feedback, we move on to the next track. This doesn't mean that there hasn't already been work done in the area, but that a track focuses on finalizing, polishing and preparing the features for release.
_Looking for Umbraco version 8? [Click here](https://github.com/umbraco/Umbraco-CMS/tree/temp8) to go to the v8 branch_
Umbraco CMS
===========
@@ -55,7 +44,7 @@ Umbraco is contribution-focused and community-driven. If you want to contribute
## Found a bug?
Another way you can contribute to Umbraco is by providing issue reports. For information on how to submit an issue report refer to our [online guide for reporting issues](CONTRIBUTING_DETAILED.md#reporting-bugs).
Another way you can contribute to Umbraco is by providing issue reports. For information on how to submit an issue report, refer to our [online guide for reporting issues](https://github.com/umbraco/Umbraco-CMS/blob/dev-v7/.github/CONTRIBUTING.md).
You can comment and report issues on the [github issue tracker](https://github.com/umbraco/Umbraco-CMS/issues).
Since [September 2018](https://umbraco.com/blog/a-second-take-on-umbraco-issue-tracker-hello-github-issues/), the old issue tracker is in read-only mode, but can still be found at [http://issues.umbraco.org](http://issues.umbraco.org).

View File

@@ -1,37 +0,0 @@
## A quick start guide for getting up and runnning with Umbraco v8
### What you need:
* [Visual Studio 2017 Community (Free)](https://www.visualstudio.com/vs/community/), or Professional, Enterprise, etc... _(**Minimum of Version 15.7** or higher, this is important, you WILL get issues with lesser versions)_
* .NET Framework 4.7.2 installed, get it here: https://www.microsoft.com/net/download/thank-you/net472?survey=false
* .NET Framework 4.7.2 developer pack, get it here: https://www.microsoft.com/net/download/thank-you/net472-developer-pack _(be sure this is the ENU file which will be named `NDP472-DevPack-ENU.exe`)_
* Clone the Umbraco repository using the `temp8` branch. If your git client doesn't support specifying the branch as you clone then use the command `git clone --single-branch -b temp8 <your fork url>`. _(If you clone the repo using the default v7 branch and then checkout the `temp8` branch you **might** get problems)_
### Start the solution
* Open the `/src/umbraco.sln` Visual Studio solution
* Start the solution (easiest way is to use `ctrl + F5`)
* When the solution is first built this may take some time since it will restore all nuget, npm and bower packages, build the .net solution and also build the angular solution
* When the website starts you'll see the Umbraco installer and just follow the prompts
* You're all set!
### Want to run from a zip instead?
If you just want to try out a few things, you can run the site from a zip file which you can download from here https://github.com/umbraco/Umbraco-CMS/releases/tag/temp8-cg18.
We recommend running the site with the Visual Studio since you'll be able to remain up to date with the latest source code changes.
### Making code changes
* _[The process for making code changes in v8 is the same as v7](https://github.com/umbraco/Umbraco-CMS/blob/dev-v7/.github/CONTRIBUTING.md)_
* Any .NET changes you make you just need to compile
* Any Angular/JS changes you make you will need to make sure you are running the Gulp build. Easiest way to do this is from within Visual Studio in the `Task Runner Explorer`. You can find this window by pressing `ctrl + q` and typing in `Task Runner Explorer`. In this window you'll see all Gulp tasks, double click on the `dev` task, this will compile the angular solution and start a file watcher, then any html/js changes you make are automatically built.
* When making js changes, you should have the chrome developer tools open to ensure that cache is disabled
* If you are only making C# changes and are not touching the UI code at all, you can significantly speed up the VS build by adding an empty file specifically called `~/src/preserve.belle`. The UI (Belle) build will then be skipped during a Rebuild.
### What to work on?
We are keeping track of [known issues and limitations here](http://issues.umbraco.org/issue/U4-11279). These line items will eventually be turned into actual tasks to be worked on. Feel free to help us keep this list updated if you find issues and even help fix some of these items. If there is a particular item you'd like to help fix please mention this on the task and we'll create a sub task for the item to continue discussion there.
There's [a list of tasks for v8 that haven't been completed](https://issues.umbraco.org/issues?q=&project=U4&tagValue=&release=8.0.0&issueType=&resolvedState=open&search=search). If you are interested in helping out with any of these please mention this on the task. This list will be constantly updated as we begin to document and design some of the other tasks that still need to get done.

86
.gitignore vendored
View File

@@ -1,49 +1,29 @@
#
# Umbraco Cms Git Ignore
#
# common files
*.obj
*.pdb
*.user
*.aps
*.pch
*.vspscc
*.orig
*.suo
*.vs10x
*.ndproj
# common directories
.DS_Store
._.DS_Store
.vs/
/local/
# build directories
[Bb]in/
[Bb]in
[Db]ebug*/
[Rr]elease*/
obj/
# tools
[Rr]elease*/
_ReSharper*/
_NCrunch_*/
*.ncrunchsolution
*.ncrunchsolution.user
*.ncrunchproject
*.crunchsolution.cache
tools/NDepend/
[Tt]est[Rr]esult*
[Bb]uild[Ll]og.*
*.[Pp]ublish.xml
*.suo
[sS]ource
[sS]andbox
umbraco.config
*.vs10x
App_Data/TEMP/*
[Uu]mbraco/[Pp]resentation/[Uu]mbraco/[Pp]lugins/*
[Uu]mbraco/[Pp]resentation/[Uu]ser[Cc]ontrols/*
@@ -66,11 +46,12 @@ src/Umbraco.Web.UI/Web.*.config.transformed
umbraco/presentation/umbraco/plugins/uComponents/uComponentsInstaller.ascx
umbraco/presentation/packages/uComponents/MultiNodePicker/CustomTreeService.asmx
*.ncrunchsolution
src/Umbraco.Tests/config/applications.config
src/Umbraco.Tests/config/trees.config
src/Umbraco.Web.UI/web.config
src/Umbraco.Web.UI/[Cc]onfig/ClientDependency.config
src/Umbraco.Web.UI/Config/ClientDependency.config
*.orig
src/Umbraco.Tests/config/404handlers.config
src/Umbraco.Web.UI/[Vv]iews/*.cshtml
src/Umbraco.Web.UI/[Vv]iews/*.vbhtml
@@ -87,15 +68,18 @@ lib-bower
src/Umbraco.Web.UI/[Uu]mbraco/[Ll]ib/*
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/umbraco.*
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/*.loader.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/init.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/routes.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/app.dev.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/loader.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/loader.dev.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/main.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/app.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/canvasdesigner.*.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/tuning.panel.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/tuning.palettes.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/tuning.loader.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/tuning.front.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/tuning.config.js
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.js
@@ -105,54 +89,56 @@ 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/
src/Umbraco.Web.UI.Client/src/[Ll]ess/*.css
tools/NDepend/
src/Umbraco.Web.UI/App_Plugins/*
src/*.psess
src/*.vspx
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/routes.js.*
NDependOut/*
*.ndproj
QueryResult.htm
*.ndproj
src/Umbraco.Web.UI/[Uu]mbraco/[Aa]ssets/*
src/Umbraco.Web.UI/[Uu]mbraco/[Ll]ib/*
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.html
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.js
src/Umbraco.Web.UI/[Cc]onfig/appSettings.config
src/Umbraco.Web.UI/[Cc]onfig/connectionStrings.config
src/Umbraco.Web.UI/[Uu]mbraco/plugins/*
src/Umbraco.Web.UI/umbraco/plugins/*
src/Umbraco.Web.UI/umbraco/js/init.js
build/ApiDocs/*
build/ApiDocs/Output/*
src/Umbraco.Web.UI.Client/bower_components/*
/src/Umbraco.Web.UI/[Uu]mbraco/preview
/src/Umbraco.Web.UI/[Uu]mbraco/preview.old
# ignore rule for clearing out Belle (avoid rebuilding all the time)
preserve.belle
/src/Umbraco.Web.UI/Umbraco/preview
/src/Umbraco.Web.UI/Umbraco/preview.old
#Ignore Rule for output of generated documentation files from Grunt docserve
src/Umbraco.Web.UI.Client/docs/api
src/*.boltdata/
/src/Umbraco.Web.UI/Umbraco/Js/canvasdesigner.loader.js
/src/Umbraco.Web.UI/Umbraco/Js/canvasdesigner.palettes.js
/src/Umbraco.Web.UI/Umbraco/Js/canvasdesigner.panel.js
/src/Umbraco.Web.UI/Umbraco/Js/canvasdesigner.config.js
/src/Umbraco.Web.UI/Umbraco/Js/canvasdesigner.front.js
src/umbraco.sln.ide/*
src/.vs/
src/Umbraco.Web.UI/[Jj]s/*
src/Umbraco.Tests/[Mm]edia
src/Umbraco.Web.UI/umbraco/js/install.loader.js
src/Umbraco.Tests/media
tools/docfx/*
apidocs/_site/*
src/*/project.lock.json
src/.idea/*
apidocs/api/*
build/docs.zip
build/ui-docs.zip
build/csharp-docs.zip
.vs/
src/packages/
src/PrecompiledWeb/*
# build
build.out/
build.tmp/
build/hooks/
build/temp/
# eof
build/Modules/*/temp/
/src/.idea/*

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!--
this is Umbraco's NuGet configuration,
content of this file is merged with the system-wide configuration,
at %APPDATA%\NuGet\NuGet.config
-->
<packageSources>
<add key="UmbracoCoreMyGet" value="https://www.myget.org/F/umbracocore/api/v3/index.json" />
<add key="ExamineAppVeyor" value="https://ci.appveyor.com/nuget/examine-f73l6qv0oqfh/" />
</packageSources>
</configuration>

62
appveyor.yml Normal file
View File

@@ -0,0 +1,62 @@
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%
SET SRC=%SLN%\src
SET PACKAGES=%SRC%\packages
CD build
SET "release="
FOR /F "skip=1 delims=" %%i IN (UmbracoVersion.txt) DO IF NOT DEFINED release SET "release=%%i"
ECHO "Restoring NuGet into %PACKAGES%"
%SRC%\.nuget\NuGet.exe sources Add -Name MyGetUmbracoCore -Source https://www.myget.org/F/umbracocore/api/v2/ >NUL
%SRC%\.nuget\NuGet.exe restore %SRC%\umbraco.sln -Verbosity Quiet -NonInteractive -PackagesDirectory %PACKAGES%
ECHO Building Release %release% build%APPVEYOR_BUILD_NUMBER%
SET PATH=C:\Program Files (x86)\MSBuild\14.0\Bin;%PATH%
SET MSBUILD="C:\Program Files (x86)\MSBuild\14.0\Bin\MsBuild.exe"
XCOPY "%SRC%\Umbraco.Tests\unit-test-log4net.CI.config" "%SRC%\Umbraco.Tests\unit-test-log4net.config" /Y
%MSBUILD% "%SLN%/src/Umbraco.Tests/Umbraco.Tests.csproj" /consoleloggerparameters:Summary;ErrorsOnly;WarningsOnly /p:NugetPackagesDirectory=%PACKAGES%
build.bat -integration -release:%release% -comment:build%APPVEYOR_BUILD_NUMBER% -nugetfolder:%PACKAGES%
test:
assemblies: src\Umbraco.Tests\bin\Debug\Umbraco.Tests.dll
artifacts:
- path: build\UmbracoCms.*
name: UmbracoFiles
- path: build\msbuild.log
name: BuildLog
deploy:
- provider: AzureBlob
storage_account_name: umbraconightlies
storage_access_key:
secure: bmEMml2SF7QLHULiePa/a01XOeIa2SxJeXuaZ+1R27b+Vb2nNUQVYiPlUyF2cZAFSHI/zO/LekRsVU1rTescGhJjF7SSjKybymI3p+F/OWpwqiu2WfFee1ofXBFx8QHw
container: umbraco-750
artifact: UmbracoFiles
on:
branch: dev-v7
notifications:
- provider: Slack
auth_token:
secure: v2csJi2V5ghR0rPdODK8GJdOGNCA+XaK84iQ9MdPOClqB+VU+40ybdKp6gPirGSH
channel: '#build-umbraco-core'
on_build_success: false
on_build_failure: true
on_build_status_changed: false

14
build.bat Normal file
View File

@@ -0,0 +1,14 @@
@ECHO OFF
powershell .\build\build.ps1
IF ERRORLEVEL 1 (
GOTO :error
) ELSE (
GOTO :EOF
)
:error
ECHO.
ECHO Cannot run build\build.ps1.
ECHO If this is due to a SecurityError then please refer to BUILD.md for help!
ECHO.

View File

@@ -0,0 +1,112 @@
#
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.com/apidocs/ui/"
$indexPath = "$src/Umbraco.Web.UI.Client/docs/api/index.html"
(Get-Content $indexPath).Replace("origin + location.href.substr(origin.length).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))
{
$source = "https://github.com/dotnet/docfx/releases/download/v2.19.2/docfx.zip"
Write-Host "Download DocFx from $source"
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"
}

View File

@@ -0,0 +1,182 @@
#
# 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"
$src = "$PSScriptRoot\..\..\..\src"
$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 -configFile "$src\NuGet.config" -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 -configFile "$src\NuGet.config" -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 -configFile "$src\NuGet.config" -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-v8.12.0-win-x86"
$source = "http://nodejs.org/dist/v8.12.0/node-v8.12.0-win-x86.7z "
if (-not (test-path $node))
{
Write-Host "Download Node..."
Invoke-WebRequest $source -OutFile "$path\node-v8.12.0-win-x86.7z"
&$sevenZip x "$path\node-v8.12.0-win-x86.7z" -o"$path" -aos > $nul
Remove-File "$path\node-v8.12.0-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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -0,0 +1,615 @@
# 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"
mkdir "$tmp\tests\Packaging" > $null
}
Copy-Files "$src\Umbraco.Tests\Packaging\Packages" "*" "$tmp\tests\Packaging\Packages"
# required for package install tests
if (-Not (Test-Path -Path "$tmp\tests\bin" ) )
{
Write-Host "Create bin directory"
mkdir "$tmp\tests\bin" > $null
}
}
#
# 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"
}
#
# 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
}
#
# 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" -configfile "$src\NuGet.config" > "$tmp\nuget.restore.log"
}
#
# Copies the Azure Gallery script to output
#
function Prepare-AzureGallery
{
param (
$uenv # an Umbraco build environment (see Get-UmbracoBuildEnv)
)
$src = "$($uenv.SolutionRoot)\src"
$tmp = "$($uenv.SolutionRoot)\build.tmp"
$out = "$($uenv.SolutionRoot)\build.out"
$psScript = "$($uenv.SolutionRoot)\build\azuregalleryrelease.ps1"
Write-Host ">> Copy azuregalleryrelease.ps1 to output folder"
Copy-Item $psScript $out
}
#
# 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 "prepare-azuregallery")
{
Prepare-AzureGallery $uenv
}
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
Prepare-AzureGallery $uenv
}
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

View File

@@ -0,0 +1,95 @@
# 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"
}
}

View File

@@ -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
}
}

View File

@@ -1,56 +1,107 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata minClientVersion="3.4.4">
<id>UmbracoCms.Core</id>
<version>8.0.0</version>
<title>Umbraco Cms Core Binaries</title>
<authors>Umbraco HQ</authors>
<owners>Umbraco HQ</owners>
<licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
<projectUrl>http://umbraco.com/</projectUrl>
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Contains the core assemblies needed to run Umbraco Cms. This package only contains assemblies and can be used for package development. Use the UmbracoCms package to setup Umbraco in Visual Studio as an ASP.NET project.</description>
<summary>Contains the core assemblies needed to run Umbraco Cms</summary>
<language>en-US</language>
<tags>umbraco</tags>
<dependencies>
<!--
note: dependencies are specified as [x.y.z,x.999999) eg [2.1.0,2.999999) and NOT [2.1.0,3.0.0) because
the latter would pick anything below 3.0.0 and that includes prereleases such as 3.0.0-alpha, and we do
not want this to happen as the alpha of the next major is, really, the next major already.
-->
<dependency id="AutoMapper" version="[7.0.1,7.999999)" />
<dependency id="LightInject" version="[5.1.2,5.999999)" />
<dependency id="LightInject.Annotation" version="[1.1.0,1.999999)" />
<dependency id="LightInject.Web" version="[2.0.0,2.999999)" />
<dependency id="Microsoft.AspNet.Identity.Core" version="[2.2.2,2.999999)" />
<dependency id="Microsoft.AspNet.WebApi.Client" version="[5.2.6,5.999999)" />
<dependency id="Microsoft.Owin" version="[4.0.0,4.999999)" />
<dependency id="MiniProfiler" version="[3.2.0.157,3.999999)" />
<dependency id="MySql.Data" version="[6.10.7,6.999999)" />
<dependency id="Newtonsoft.Json" version="[11.0.2,11.999999)" />
<dependency id="Semver" version="[2.0.4,2.999999)" />
<dependency id="Serilog" version="[2.7.1,2.999999)" />
<dependency id="Serilog.Enrichers.Process" version="[2.0.1,2.999999)" />
<dependency id="Serilog.Enrichers.Thread" version="[3.0.0,3.999999)" />
<dependency id="Serilog.Filters.Expressions" version="[2.0.0,2.999999)" />
<dependency id="Serilog.Formatting.Compact" version="[1.0.0,1.999999)" />
<dependency id="Serilog.Settings.AppSettings" version="[2.1.2,2.999999)" />
<dependency id="Serilog.Sinks.File" version="[4.0.0,4.999999)" />
<dependency id="Umbraco.SqlServerCE" version="[4.0.0.1,4.999999)" />
<dependency id="NPoco" version="[3.9.4,3.999999)" />
</dependencies>
</metadata>
<files>
<!-- libs -->
<file src="$BuildTmp$\WebApp\bin\Umbraco.Core.dll" target="lib\net472\Umbraco.Core.dll" />
<!-- docs -->
<file src="$BuildTmp$\WebApp\bin\Umbraco.Core.xml" target="lib\Umbraco.Core.xml" />
<!-- symbols -->
<file src="$BuildTmp$\bin\Umbraco.Core.pdb" target="lib" />
<file src="$BuildTmp$\..\src\Umbraco.Core\**\*.cs" exclude="$BuildTmp$\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Core" />
</files>
</package>
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata minClientVersion="3.4.4">
<id>UmbracoCms.Core</id>
<version>7.0.0</version>
<title>Umbraco Cms Core Binaries</title>
<authors>Umbraco HQ</authors>
<owners>Umbraco HQ</owners>
<licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
<projectUrl>http://umbraco.com/</projectUrl>
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Contains the core assemblies needed to run Umbraco Cms. This package only contains assemblies and can be used for package development. Use the UmbracoCms-package to setup Umbraco in Visual Studio as an ASP.NET project.</description>
<summary>Contains the core assemblies needed to run Umbraco Cms</summary>
<language>en-US</language>
<tags>umbraco</tags>
<dependencies>
<dependency id="log4net" version="[2.0.8,3.0.0)" />
<dependency id="Log4Net.Async" version="[2.0.4,3.0.0)" />
<dependency id="Microsoft.AspNet.Mvc" version="[5.2.3,6.0.0)" />
<dependency id="Microsoft.AspNet.WebApi" version="[5.2.3,6.0.0)" />
<dependency id="Microsoft.AspNet.Identity.Owin" version="[2.2.1, 3.0.0)" />
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.2.1, 3.0.0)" />
<dependency id="Microsoft.Owin.Security.Cookies" version="[3.1.0, 4.0.0)" />
<dependency id="Microsoft.Owin.Security.OAuth" version="[3.1.0, 4.0.0)" />
<dependency id="Microsoft.Owin.Host.SystemWeb" version="[3.1.0, 4.0.0)" />
<dependency id="MiniProfiler" version="[2.1.0, 3.0.0)" />
<dependency id="HtmlAgilityPack" version="[1.4.9.5, 2.0.0)" />
<dependency id="Lucene.Net" version="[2.9.4.1, 3.0.0.0)" />
<dependency id="MySql.Data" version="[6.9.9, 7.0.0)" />
<dependency id="ClientDependency" version="[1.9.7, 2.0.0)" />
<dependency id="ClientDependency-Mvc5" version="[1.8.0.0, 2.0.0)" />
<dependency id="AutoMapper" version="[3.3.1, 4.0.0)" />
<dependency id="Newtonsoft.Json" version="[10.0.2, 11.0.0)" />
<dependency id="Examine" version="[0.1.89, 1.0.0)" />
<dependency id="ImageProcessor" version="[2.5.6, 3.0.0)" />
<dependency id="ImageProcessor.Web" version="[4.8.7, 5.0.0)" />
<dependency id="semver" version="[1.1.2, 3.0.0)" />
<!-- Markdown can not be updated due to: https://github.com/hey-red/markdownsharp/issues/71#issuecomment-233585487 -->
<dependency id="Markdown" version="[1.14.7, 2.0.0)" />
<dependency id="System.Threading.Tasks.Dataflow" version="[4.7.0, 5.0.0)" />
<dependency id="System.ValueTuple" version="[4.4.0, 5.0.0)" />
</dependencies>
</metadata>
<files>
<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="$BuildTmp$\bin\SQLCE4Umbraco.pdb" target="lib" />
<file src="..\..\src\SQLCE4Umbraco\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\SQLCE4Umbraco" />
<file src="$BuildTmp$\bin\businesslogic.pdb" target="lib" />
<file src="..\..\src\umbraco.businesslogic\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.businesslogic" />
<file src="$BuildTmp$\bin\cms.pdb" target="lib" />
<file src="..\..\src\umbraco.cms\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.cms" />
<file src="$BuildTmp$\bin\controls.pdb" target="lib" />
<file src="..\..\src\umbraco.controls\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.controls" />
<file src="$BuildTmp$\bin\interfaces.pdb" target="lib" />
<file src="..\..\src\umbraco.interfaces\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.interfaces" />
<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="$BuildTmp$\bin\umbraco.DataLayer.pdb" target="lib" />
<file src="..\..\src\umbraco.datalayer\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.datalayer" />
<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="$BuildTmp$\bin\umbraco.MacroEngines.pdb" target="lib" />
<file src="..\..\src\umbraco.MacroEngines\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\umbraco.MacroEngines" />
<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="$BuildTmp$\bin\umbraco.pdb" target="lib" />
<file src="..\..\src\Umbraco.Web\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Web" />
<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="$BuildTmp$\bin\UmbracoExamine.pdb" target="lib" />
<file src="..\..\src\UmbracoExamine\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\UmbracoExamine" />
</files>
</package>

View File

@@ -1,62 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata minClientVersion="3.4.4">
<id>UmbracoCms.Web</id>
<version>8.0.0</version>
<title>Umbraco Cms Core Binaries</title>
<authors>Umbraco HQ</authors>
<owners>Umbraco HQ</owners>
<licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
<projectUrl>http://umbraco.com/</projectUrl>
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Contains the web assemblies needed to run Umbraco Cms. This package only contains assemblies and can be used for package development. Use the UmbracoCms package to setup Umbraco in Visual Studio as an ASP.NET project.</description>
<summary>Contains the core assemblies needed to run Umbraco Cms</summary>
<language>en-US</language>
<tags>umbraco</tags>
<dependencies>
<!--
note: dependencies are specified as [x.y.z,x.999999) eg [2.1.0,2.999999) and NOT [2.1.0,3.0.0) because
the latter would pick anything below 3.0.0 and that includes prereleases such as 3.0.0-alpha, and we do
not want this to happen as the alpha of the next major is, really, the next major already.
-->
<dependency id="UmbracoCms.Core" version="[$version$]" />
<dependency id="AutoMapper" version="[7.0.1,7.999999)" />
<dependency id="ClientDependency" version="[1.9.7,1.999999)" />
<dependency id="ClientDependency-Mvc5" version="[1.8.0,1.999999)" />
<dependency id="CSharpTest.Net.Collections" version="[14.906.1403.1082,14.999999)" />
<dependency id="Examine" version="[1.0.0-beta025,1.999999)" />
<dependency id="Lucene.Net.Contrib" version="[3.0.3,3.999999)" />
<dependency id="HtmlAgilityPack" version="[1.8.9,1.999999)" />
<dependency id="ImageProcessor" version="[2.6.2.25,2.999999)" />
<dependency id="LightInject.Mvc" version="[2.0.0,2.999999)" />
<dependency id="LightInject.WebApi" version="[2.0.0,2.999999)" />
<dependency id="Markdown" version="[2.2.1,2.999999)" />
<dependency id="Microsoft.AspNet.Identity.Owin" version="[2.2.2,2.999999)" />
<dependency id="Microsoft.AspNet.Mvc" version="[5.2.6,5.999999)" />
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.2.3,2.999999)" />
<dependency id="Microsoft.AspNet.WebApi" version="[5.2.6,5.999999)" />
<dependency id="Microsoft.Owin.Host.SystemWeb" version="[4.0.0,4.999999)" />
<dependency id="Microsoft.Owin.Security.Cookies" version="[4.0.0,4.999999)" />
<dependency id="Microsoft.Owin.Security.OAuth" version="[4.0.0,4.999999)" />
<dependency id="System.Threading.Tasks.Dataflow" version="[4.8.0,4.999999)" />
</dependencies>
</metadata>
<files>
<!-- libs -->
<file src="$BuildTmp$\WebApp\bin\Umbraco.Web.dll" target="lib\net472\Umbraco.Web.dll" />
<file src="$BuildTmp$\WebApp\bin\Umbraco.Web.UI.dll" target="lib\net472\Umbraco.Web.UI.dll" />
<file src="$BuildTmp$\WebApp\bin\Umbraco.Examine.dll" target="lib\net472\Umbraco.Examine.dll" />
<!-- docs -->
<file src="$BuildTmp$\WebApp\bin\Umbraco.Web.xml" target="lib\Umbraco.Web.xml" />
<file src="$BuildTmp$\WebApp\bin\Umbraco.Web.UI.xml" target="lib\Umbraco.Web.UI.xml" />
<file src="$BuildTmp$\WebApp\bin\Umbraco.Examine.xml" target="lib\Umbraco.Examine.xml" />
<!-- symbols -->
<file src="$BuildTmp$\bin\Umbraco.Web.pdb" target="lib" />
<file src="$BuildTmp$\..\src\Umbraco.Web\**\*.cs" exclude="$BuildTmp$\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Web" />
<file src="$BuildTmp$\bin\Umbraco.Examine.pdb" target="lib" />
<file src="$BuildTmp$\..\src\Umbraco.Examine\**\*.cs" exclude="$BuildTmp$\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Examine" />
</files>
</package>

View File

@@ -1,65 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata minClientVersion="3.4.4">
<id>UmbracoCms</id>
<version>8.0.0</version>
<title>Umbraco Cms</title>
<authors>Umbraco HQ</authors>
<owners>Umbraco HQ</owners>
<licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
<projectUrl>http://umbraco.com/</projectUrl>
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Installs Umbraco Cms in your Visual Studio ASP.NET project</description>
<summary>Installs Umbraco Cms in your Visual Studio ASP.NET project</summary>
<language>en-US</language>
<tags>umbraco</tags>
<dependencies>
<dependency id="UmbracoCms.Web" version="[$version$]" />
<!--
note: dependencies are specified as [x.y.z,x.999999) eg [2.1.0,2.999999) and NOT [2.1.0,3.0.0) because
the latter would pick anything below 3.0.0 and that includes prereleases such as 3.0.0-alpha, and we do
not want this to happen as the alpha of the next major is, really, the next major already.
-->
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.2.3, 2.999999)" />
<dependency id="Umbraco.ModelsBuilder.Ui" version="[8.0.0-alpha.24]" />
<dependency id="ImageProcessor.Web" version="[4.9.3.25,4.999999)" />
<dependency id="ImageProcessor.Web.Config" version="[2.4.1.19,2.999999)" />
<dependency id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="[2.0.0,2.999999)" />
<dependency id="Microsoft.Net.Compilers" version="[2.9.0,2.999999)" />
</dependencies>
</metadata>
<files>
<!-- files -->
<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\config\BackOfficeTours\**" target="Content\Config\BackOfficeTours" />
<file src="$BuildTmp$\WebApp\Media\Web.config" target="Content\Media\Web.config" />
<!-- these files are copied by install.ps1 -->
<file src="$BuildTmp$\WebApp\Web.config" target="UmbracoFiles\Web.config" />
<file src="$BuildTmp$\WebApp\umbraco\**" target="UmbracoFiles\umbraco" />
<file src="$BuildTmp$\WebApp\config\splashes\**" target="UmbracoFiles\Config\splashes" />
<!-- tools -->
<!-- beware! install.ps1 not supported by PackageReference -->
<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" />
<!-- config transforms -->
<!-- beware! config transforms not supported by PackageReference -->
<file src="tools\Web.config.install.xdt" target="Content\Web.config.install.xdt" />
<file src="tools\applications.config.install.xdt" target="Content\config\applications.config.install.xdt" />
<file src="tools\ClientDependency.config.install.xdt" target="Content\config\ClientDependency.config.install.xdt" />
<file src="tools\Dashboard.config.install.xdt" target="Content\config\Dashboard.config.install.xdt" />
<file src="tools\trees.config.install.xdt" target="Content\config\trees.config.install.xdt" />
<file src="tools\umbracoSettings.config.install.xdt" target="Content\config\umbracoSettings.config.install.xdt" />
<file src="tools\Views.Web.config.install.xdt" target="Views\Web.config.install.xdt" /> <!-- FIXME Content\ !! and then... transform?! -->
<!-- UmbracoCms props and targets -->
<file src="build\**" target="build" />
</files>
</package>
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata minClientVersion="3.4.4">
<id>UmbracoCms</id>
<version>7.0.0</version>
<title>Umbraco Cms</title>
<authors>Umbraco HQ</authors>
<owners>Umbraco HQ</owners>
<licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
<projectUrl>http://umbraco.com/</projectUrl>
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Installs Umbraco Cms in your Visual Studio ASP.NET project</description>
<summary>Installs Umbraco Cms in your Visual Studio ASP.NET project</summary>
<language>en-US</language>
<tags>umbraco</tags>
<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.10, 4.0.0)" />
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.2.1, 3.0.0)" />
<dependency id="ImageProcessor.Web.Config" version="[2.3.1, 3.0.0)" />
<dependency id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="[2.0.0, 3.0.0)" />
</dependencies>
</metadata>
<files>
<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\config\BackOfficeTours\**" target="Content\Config\BackOfficeTours" />
<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" />
<file src="tools\Web.config.install.xdt" target="Content\Web.config.install.xdt" />
<file src="tools\applications.config.install.xdt" target="Content\config\applications.config.install.xdt" />
<file src="tools\ClientDependency.config.install.xdt" target="Content\config\ClientDependency.config.install.xdt" />
<file src="tools\Dashboard.config.install.xdt" target="Content\config\Dashboard.config.install.xdt" />
<file src="tools\trees.config.install.xdt" target="Content\config\trees.config.install.xdt" />
<file src="tools\umbracoSettings.config.install.xdt" target="Content\config\umbracoSettings.config.install.xdt" />
<file src="tools\Views.Web.config.install.xdt" target="Views\Web.config.install.xdt" />
<file src="tools\processing.config.install.xdt" target="Content\Config\imageprocessor\processing.config.install.xdt" />
<file src="tools\cache.config.install.xdt" target="Content\Config\imageprocessor\cache.config.install.xdt" />
<file src="build\**" target="build" />
</files>
</package>

View File

@@ -47,6 +47,9 @@
<CustomFilesToInclude Include=".\umbraco\**\*">
<Dir>umbraco</Dir>
</CustomFilesToInclude>
<CustomFilesToInclude Include=".\umbraco_client\**\*">
<Dir>umbraco_client</Dir>
</CustomFilesToInclude>
<CustomFilesToInclude Include=".\Global.asax">
<Dir>.</Dir>
</CustomFilesToInclude>

View File

@@ -1,26 +1,26 @@
_ _ __ __ ____ _____ _____ ____
| | | | \/ | _ \| __ \ /\ / ____/ __ \
| | | | \ / | |_) | |__) | / \ | | | | | |
| | | | |\/| | _ <| _ / / /\ \| | | | | |
| |__| | | | | |_) | | \ \ / ____ | |___| |__| |
\____/|_| |_|____/|_| \_/_/ \_\_____\____/
----------------------------------------------------
Don't forget to build!
When upgrading your website using NuGet you should answer "No" to the questions to overwrite the Web.config
file (and config files in the config folder).
This NuGet package includes build targets that extend the creation of a deploy package, which is generated by
Publishing from Visual Studio. The targets will only work once Publishing is configured, so if you don't use
Publish this won't affect you.
The following items will now be automatically included when creating a deploy package or publishing to the file
system: umbraco, config\splashes and global.asax.
Please read the release notes on our.umbraco.com:
https://our.umbraco.com/download/releases
- Umbraco
_ _ __ __ ____ _____ _____ ____
| | | | \/ | _ \| __ \ /\ / ____/ __ \
| | | | \ / | |_) | |__) | / \ | | | | | |
| | | | |\/| | _ <| _ / / /\ \| | | | | |
| |__| | | | | |_) | | \ \ / ____ | |___| |__| |
\____/|_| |_|____/|_| \_/_/ \_\_____\____/
----------------------------------------------------
Don't forget to build!
When upgrading your website using NuGet you should answer "No" to the questions to overwrite the Web.config
file (and config files in the config folder).
This NuGet package includes build targets that extend the creation of a deploy package, which is generated by
Publishing from Visual Studio. The targets will only work once Publishing is configured, so if you don't use
Publish this won't affect you.
The following items will now be automatically included when creating a deploy package or publishing to the file
system: umbraco, umbraco_client, config\splashes and global.asax.
Please read the release notes on our.umbraco.com:
https://our.umbraco.com/download/releases
- Umbraco

View File

@@ -1,30 +1,39 @@
_ _ __ __ ____ _____ _____ ____
| | | | \/ | _ \| __ \ /\ / ____/ __ \
_ _ __ __ ____ _____ _____ ____
| | | | \/ | _ \| __ \ /\ / ____/ __ \
| | | | \ / | |_) | |__) | / \ | | | | | |
| | | | |\/| | _ <| _ / / /\ \| | | | | |
| |__| | | | | |_) | | \ \ / ____ | |___| |__| |
\____/|_| |_|____/|_| \_/_/ \_\_____\____/
\____/|_| |_|____/|_| \_/_/ \_\_____\____/
----------------------------------------------------
*** IMPORTANT NOTICE FOR UPGRADES FROM VERSIONS BELOW 7.7.0 ***
Be sure to read the version specific upgrade information before proceeding:
https://our.umbraco.com/documentation/Getting-Started/Setup/Upgrading/version-specific#version-7-7-0
Depending on the version you are upgrading from, you may need to make some changes to your web.config
and you will need to be aware of the breaking changes listed there to see if these affect your installation.
Don't forget to build!
We've done our best to transform your configuration files but in case something is not quite right: remember we
backed up your files in App_Data\NuGetBackup so you can find the original files before they were transformed.
We've overwritten all the files in the Umbraco folder, these have been backed up in
App_Data\NuGetBackup. We didn't overwrite the UI.xml file nor did we remove any files or folders that you or
a package might have added. Only the existing files were overwritten. If you customized anything then make
We've overwritten all the files in the Umbraco and Umbraco_Client folder, these have been backed up in
App_Data\NuGetBackup. We didn't overwrite the UI.xml file nor did we remove any files or folders that you or
a package might have added. Only the existing files were overwritten. If you customized anything then make
sure to do a compare and merge with the NuGetBackup folder.
This NuGet package includes build targets that extend the creation of a deploy package, which is generated by
This NuGet package includes build targets that extend the creation of a deploy package, which is generated by
Publishing from Visual Studio. The targets will only work once Publishing is configured, so if you don't use
Publish this won't affect you.
The following items will now be automatically included when creating a deploy package or publishing to the file
system: umbraco, config\splashes and global.asax.
The following items will now be automatically included when creating a deploy package or publishing to the file
system: umbraco, umbraco_client, config\splashes and global.asax.
Please read the release notes on our.umbraco.com:
http://our.umbraco.com/contribute/releases
- Umbraco
- Umbraco

View File

@@ -1,204 +1,420 @@
<?xml version="1.0"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<configSections xdt:Transform="InsertIfMissing" />
<configSections>
<section name="FileSystemProviders" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<section name="ExamineLuceneIndexSets" type="Umbraco.Examine.Config.IndexSets, Umbraco.Examine" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="SetAttributes(type,requirePermission)" />
<configSections xdt:Transform="InsertIfMissing" />
<configSections>
<section name="BaseRestExtensions" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<section name="FileSystemProviders" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<section name="ExamineLuceneIndexSets" type="Examine.LuceneEngine.Config.IndexSets, Examine" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="SetAttributes(type,requirePermission)" />
<sectionGroup name="applicationSettings" xdt:Locator="Match(name)">
<section name="umbraco.presentation.Properties.Settings" xdt:Locator="Match(name)" xdt:Transform="Remove" />
</sectionGroup>
<sectionGroup name="system.web.webPages.razor" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<sectionGroup name="umbracoConfiguration" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing">
<section name="settings" type="Umbraco.Core.Configuration.UmbracoSettings.UmbracoSettingsSection, Umbraco.Core" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
<section name="FileSystemProviders" type="Umbraco.Core.Configuration.FileSystemProvidersSection, Umbraco.Core" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
<section name="dashBoard" type="Umbraco.Core.Configuration.Dashboard.DashboardSection, Umbraco.Core" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
<section name="HealthChecks" type="Umbraco.Core.Configuration.HealthChecks.HealthChecksSection, Umbraco.Core" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
</sectionGroup>
</configSections>
<sectionGroup name="applicationSettings" xdt:Locator="Match(name)">
<section name="umbraco.presentation.Properties.Settings" xdt:Locator="Match(name)" xdt:Transform="Remove" />
</sectionGroup>
<sectionGroup name="system.web.webPages.razor" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<sectionGroup name="umbracoConfiguration" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing">
<section name="settings" type="Umbraco.Core.Configuration.UmbracoSettings.UmbracoSettingsSection, Umbraco.Core" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
<section name="BaseRestExtensions" type="Umbraco.Core.Configuration.BaseRest.BaseRestSection, Umbraco.Core" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
<section name="FileSystemProviders" type="Umbraco.Core.Configuration.FileSystemProvidersSection, Umbraco.Core" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
<section name="dashBoard" type="Umbraco.Core.Configuration.Dashboard.DashboardSection, Umbraco.Core" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
<section name="HealthChecks" type="Umbraco.Core.Configuration.HealthChecks.HealthChecksSection, Umbraco.Core" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
</sectionGroup>
</configSections>
<appSettings xdt:Transform="InsertIfMissing" />
<appSettings>
<add key="owin:appStartup" value="UmbracoDefaultOwinStartup" xdt:Locator="Match(key)" xdt:Transform="InsertIfMissing" />
<add key="Umbraco.ModelsBuilder.Enable" value="false" xdt:Locator="Match(key)" xdt:Transform="InsertIfMissing" />
<add key="Umbraco.ModelsBuilder.ModelsMode" value="Nothing" xdt:Locator="Match(key)" xdt:Transform="InsertIfMissing" />
<add key="umbracoDefaultUILanguage" value="en-US" xdt:Locator="Match(key)" xdt:Transform="SetAttributes(value)" />
</appSettings>
<appSettings xdt:Transform="InsertIfMissing" />
<appSettings>
<add key="owin:appStartup" value="UmbracoDefaultOwinStartup" xdt:Locator="Match(key)" xdt:Transform="InsertIfMissing" />
<add key="Umbraco.ModelsBuilder.Enable" value="false" xdt:Locator="Match(key)" xdt:Transform="InsertIfMissing" />
<add key="Umbraco.ModelsBuilder.ModelsMode" value="Nothing" xdt:Locator="Match(key)" xdt:Transform="InsertIfMissing" />
<add key="umbracoDefaultUILanguage" value="en-US" xdt:Locator="Match(key)" xdt:Transform="SetAttributes(value)" />
</appSettings>
<umbracoConfiguration xdt:Transform="InsertIfMissing">
<settings configSource="config\umbracoSettings.config" xdt:Locator="Match(configSource)" xdt:Transform="InsertIfMissing" />
<FileSystemProviders configSource="config\FileSystemProviders.config" xdt:Locator="Match(configSource)" xdt:Transform="InsertIfMissing" />
<dashBoard configSource="config\Dashboard.config" xdt:Locator="Match(configSource)" xdt:Transform="InsertIfMissing" />
<HealthChecks configSource="config\HealthChecks.config" xdt:Locator="Match(configSource)" xdt:Transform="InsertIfMissing" />
</umbracoConfiguration>
<umbracoConfiguration xdt:Transform="InsertIfMissing">
<settings configSource="config\umbracoSettings.config" xdt:Locator="Match(configSource)" xdt:Transform="InsertIfMissing" />
<BaseRestExtensions configSource="config\BaseRestExtensions.config" xdt:Locator="Match(configSource)" xdt:Transform="InsertIfMissing" />
<FileSystemProviders configSource="config\FileSystemProviders.config" xdt:Locator="Match(configSource)" xdt:Transform="InsertIfMissing" />
<dashBoard configSource="config\Dashboard.config" xdt:Locator="Match(configSource)" xdt:Transform="InsertIfMissing" />
<HealthChecks configSource="config\HealthChecks.config" xdt:Locator="Match(configSource)" xdt:Transform="InsertIfMissing" />
</umbracoConfiguration>
<FileSystemProviders xdt:Transform="Remove" />
<FileSystemProviders xdt:Transform="Remove" />
<BaseRestExtensions xdt:Transform="Remove" />
<system.data xdt:Transform="InsertIfMissing">
<DbProviderFactories xdt:Transform="InsertIfMissing">
<remove invariant="System.Data.SqlServerCe.4.0" xdt:Locator="Match(invariant)" xdt:Transform="InsertIfMissing" />
<add name="Microsoft SQL Server Compact Data Provider 4.0" invariant="System.Data.SqlServerCe.4.0" description=".NET Framework Data Provider for Microsoft SQL Server Compact" type="System.Data.SqlServerCe.SqlCeProviderFactory, System.Data.SqlServerCe" xdt:Locator="Match(invariant)" xdt:Transform="SetAttributes(invariant,description,type)" />
<remove invariant="MySql.Data.MySqlClient" xdt:Locator="Match(invariant)" xdt:Transform="InsertIfMissing" />
<add invariant="MySql.Data.MySqlClient" xdt:Locator="Match(invariant)" xdt:Transform="Remove" />
<add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data" xdt:Locator="Match(invariant)" xdt:Transform="InsertIfMissing" />
<add invariant="MySql.Data.MySqlClient" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data" xdt:Locator="Match(invariant)" xdt:Transform="SetAttributes(type)" />
</DbProviderFactories>
</system.data>
<clientDependency xdt:Transform="RemoveAttributes(version)" />
<system.data xdt:Transform="InsertIfMissing">
<DbProviderFactories xdt:Transform="InsertIfMissing">
<remove invariant="System.Data.SqlServerCe.4.0" xdt:Locator="Match(invariant)" xdt:Transform="InsertIfMissing" />
<add name="Microsoft SQL Server Compact Data Provider 4.0" invariant="System.Data.SqlServerCe.4.0" description=".NET Framework Data Provider for Microsoft SQL Server Compact" type="System.Data.SqlServerCe.SqlCeProviderFactory, System.Data.SqlServerCe" xdt:Locator="Match(invariant)" xdt:Transform="SetAttributes(invariant,description,type)" />
<remove invariant="MySql.Data.MySqlClient" xdt:Locator="Match(invariant)" xdt:Transform="InsertIfMissing" />
<add invariant="MySql.Data.MySqlClient" xdt:Locator="Match(invariant)" xdt:Transform="Remove" />
<add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data" xdt:Locator="Match(invariant)" xdt:Transform="InsertIfMissing" />
<add invariant="MySql.Data.MySqlClient" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data" xdt:Locator="Match(invariant)" xdt:Transform="SetAttributes(type)" />
</DbProviderFactories>
</system.data>
<system.web xdt:Transform="InsertIfMissing" />
<system.web>
<siteMap xdt:Transform="Remove" />
<siteMap xdt:Transform="InsertIfMissing">
<providers xdt:Transform="InsertIfMissing">
<remove name="MySqlSiteMapProvider" xdt:Transform="InsertIfMissing" />
</providers>
</siteMap>
<httpRuntime xdt:Transform="InsertIfMissing" />
<httpRuntime maxRequestLength="51200" fcnMode="Single" xdt:Transform="SetAttributes(fcnMode,maxRequestLength)" />
<httpRuntime targetFramework="4.5" xdt:Locator="Condition(count(@targetFramework) != 1)" xdt:Transform="SetAttributes(targetFramework)" />
<clientDependency xdt:Transform="RemoveAttributes(version)" />
<membership defaultProvider="DefaultMembershipProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove" />
<roleManager defaultProvider="DefaultRoleProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove"/>
<profile defaultProvider="DefaultProfileProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove"/>>
<sessionState customProvider="DefaultSessionProvider" xdt:Locator="Match(customProvider)" xdt:Transform="Remove"/>
<compilation xdt:Transform="InsertIfMissing" />
<system.web xdt:Transform="InsertIfMissing" />
<system.web>
<siteMap xdt:Transform="Remove" />
<siteMap xdt:Transform="InsertIfMissing">
<providers xdt:Transform="InsertIfMissing">
<remove name="MySqlSiteMapProvider" xdt:Transform="InsertIfMissing" />
</providers>
</siteMap>
<httpRuntime xdt:Transform="InsertIfMissing" />
<httpRuntime maxRequestLength="51200" fcnMode="Single" xdt:Transform="SetAttributes(fcnMode,maxRequestLength)" />
<httpRuntime targetFramework="4.5" xdt:Locator="Condition(count(@targetFramework) != 1)" xdt:Transform="SetAttributes(targetFramework)" />
<xhtmlConformance xdt:Transform="Remove" />
<membership defaultProvider="DefaultMembershipProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove" />
<roleManager defaultProvider="DefaultRoleProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove"/>
<profile defaultProvider="DefaultProfileProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove"/>>
<sessionState customProvider="DefaultSessionProvider" xdt:Locator="Match(customProvider)" xdt:Transform="Remove"/>
<compilation xdt:Transform="InsertIfMissing" />
<compilation>
<assemblies xdt:Transform="InsertIfMissing" />
<assemblies>
<add assembly="System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Web.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<add assembly="System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<add assembly="System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<add assembly="System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<add assembly="System.Data.DataSetExtensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<add assembly="System.Web.Extensions.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<add assembly="System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<add assembly="System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<add assembly="System.Web.WebPages, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<add assembly="System.Collections, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Collections.Concurrent, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.ComponentModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.ComponentModel.Annotations, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.ComponentModel.EventBasedAsync, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Diagnostics.Contracts, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Diagnostics.Debug, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Diagnostics.Tools, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Diagnostics.Tracing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Dynamic.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Globalization, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.IO, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Linq.Expressions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Linq.Parallel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Linq.Queryable, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Net.NetworkInformation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Net.Primitives, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Net.Requests, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.ObjectModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Reflection, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Reflection.Emit, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Reflection.Emit.ILGeneration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Reflection.Emit.Lightweight, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Reflection.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Reflection.Primitives, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Resources.ResourceManager, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Runtime.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Runtime.InteropServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Runtime.InteropServices.WindowsRuntime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Runtime.Numerics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Runtime.Serialization.Json, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Runtime.Serialization.Primitives, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Runtime.Serialization.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Security.Principal, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.ServiceModel.Duplex, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.ServiceModel.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.ServiceModel.NetTcp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.ServiceModel.Primitives, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.ServiceModel.Security, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Text.Encoding, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Text.Encoding.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Text.RegularExpressions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Threading, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Threading.Tasks, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Threading.Tasks.Parallel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Xml.ReaderWriter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Xml.XDocument, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
<add assembly="System.Xml.XmlSerializer, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" xdt:Locator="Match(assembly)" xdt:Transform="InsertIfMissing" />
</assemblies>
</compilation>
<httpModules xdt:Transform="InsertIfMissing" />
<httpModules>
<add name="umbracoRequestModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<add name="umbracoBaseRequestModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<add name="viewstateMoverModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<add name=" UmbracoModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<add name="UmbracoModule" type="Umbraco.Web.UmbracoModule,umbraco" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
</httpModules>
<compilation>
<assemblies>
<remove assembly="System.Web.Http" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Net.Http" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Runtime" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Collections" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Collections.Concurrent" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.ComponentModel" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.ComponentModel.Annotations" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.ComponentModel.EventBasedAsync" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Diagnostics.Contracts" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Diagnostics.Debug" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Diagnostics.Tools" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Diagnostics.Tracing" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Dynamic.Runtime" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Globalization" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.IO" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Linq" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Linq.Expressions" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Linq.Parallel" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Linq.Queryable" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Net.NetworkInformation" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Net.Primitives" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Net.Requests" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.ObjectModel" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Reflection" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Reflection.Emit" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Reflection.Emit.ILGeneration" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Reflection.Emit.Lightweight" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Reflection.Extensions" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Reflection.Primitives" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Resources.ResourceManager" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Runtime" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Runtime.Extensions" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Runtime.InteropServices" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Runtime.InteropServices.WindowsRuntime" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Runtime.Numerics" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Runtime.Serialization.Json" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Runtime.Serialization.Primitives" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Runtime.Serialization.Xml" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Security.Principal" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.ServiceModel.Duplex" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.ServiceModel.Http" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.ServiceModel.NetTcp" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.ServiceModel.Primitives" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.ServiceModel.Security" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Text.Encoding" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Text.Encoding.Extensions" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Text.RegularExpressions" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Threading" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Threading.Tasks" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Threading.Tasks.Parallel" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Xml.ReaderWriter" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Xml.XDocument" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
<remove assembly="System.Xml.XmlSerializer" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
</assemblies>
</compilation>
<compilation>
<assemblies>
<remove assembly="System.Web.Http" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Net.Http" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Runtime" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Collections" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Collections.Concurrent" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.ComponentModel" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.ComponentModel.Annotations" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.ComponentModel.EventBasedAsync" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Diagnostics.Contracts" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Diagnostics.Debug" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Diagnostics.Tools" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Diagnostics.Tracing" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Dynamic.Runtime" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Globalization" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.IO" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Linq" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Linq.Expressions" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Linq.Parallel" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Linq.Queryable" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Net.NetworkInformation" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Net.Primitives" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Net.Requests" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.ObjectModel" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Reflection" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Reflection.Emit" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Reflection.Emit.ILGeneration" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Reflection.Emit.Lightweight" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Reflection.Extensions" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Reflection.Primitives" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Resources.ResourceManager" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Runtime" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Runtime.Extensions" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Runtime.InteropServices" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Runtime.InteropServices.WindowsRuntime" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Runtime.Numerics" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Runtime.Serialization.Json" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Runtime.Serialization.Primitives" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Runtime.Serialization.Xml" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Security.Principal" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.ServiceModel.Duplex" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.ServiceModel.Http" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.ServiceModel.NetTcp" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.ServiceModel.Primitives" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.ServiceModel.Security" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Text.Encoding" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Text.Encoding.Extensions" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Text.RegularExpressions" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Threading" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Threading.Tasks" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Threading.Tasks.Parallel" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Xml.ReaderWriter" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Xml.XDocument" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
<remove assembly="System.Xml.XmlSerializer" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
</assemblies>
</compilation>
<httpHandlers xdt:Transform="InsertIfMissing" />
<httpHandlers>
<add path="GoogleSpellChecker.ashx" xdt:Locator="Match(path)" xdt:Transform="Remove" />
</httpHandlers>
</system.web>
<xhtmlConformance xdt:Transform="Remove" />
<system.webServer xdt:Transform="InsertIfMissing" />
<system.webServer>
<modules xdt:Transform="InsertIfMissing" />
<modules runAllManagedModulesForAllRequests="true" xdt:Transform="SetAttributes(runAllManagedModulesForAllRequests)">
<remove name="umbracoRequestModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<remove name="viewstateMoverModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<remove name="umbracoBaseRequestModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<httpModules xdt:Transform="InsertIfMissing" />
<httpModules>
<add name="umbracoRequestModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<add name="umbracoBaseRequestModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<add name="viewstateMoverModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<add name=" UmbracoModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<add name="UmbracoModule" type="Umbraco.Web.UmbracoModule,umbraco" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
</httpModules>
<add name="umbracoRequestModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<add name="viewstateMoverModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<add name="umbracoBaseRequestModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<!-- Note, we're removing the one that starts with a space here, don't correct it -->
<!-- This to fix a quirk we for a lot of releases where we added it with the space by default -->
<add name=" UmbracoModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<add name="UmbracoModule" type="Umbraco.Web.UmbracoModule,umbraco" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
</modules>
<httpHandlers xdt:Transform="InsertIfMissing" />
<httpHandlers>
<add path="GoogleSpellChecker.ashx" xdt:Locator="Match(path)" xdt:Transform="Remove" />
</httpHandlers>
</system.web>
<staticContent xdt:Transform="InsertIfMissing" />
<staticContent>
<remove fileExtension=".svg" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
<mimeMap fileExtension=".svg" mimeType="image/svg+xml" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
<remove fileExtension=".woff" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
<mimeMap fileExtension=".woff" mimeType="application/x-font-woff" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
<remove fileExtension=".woff2" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
<mimeMap fileExtension=".woff2" mimeType="application/x-font-woff2" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
<remove fileExtension=".less" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
<mimeMap fileExtension=".less" mimeType="text/css" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
</staticContent>
<system.webServer xdt:Transform="InsertIfMissing" />
<system.webServer>
<modules xdt:Transform="InsertIfMissing" />
<modules runAllManagedModulesForAllRequests="true" xdt:Transform="SetAttributes(runAllManagedModulesForAllRequests)">
<remove name="umbracoRequestModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<remove name="viewstateMoverModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<remove name="umbracoBaseRequestModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<remove name="WebDAVModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<!-- Inserts it as the first element. Also see http://stackoverflow.com/a/19041487/5018 -->
<remove name="WebDAVModule" xdt:Locator="Match(name)" xdt:Transform="Insert" />
<handlers>
<remove name="SpellChecker" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<add name="SpellChecker" xdt:Locator="Match(name)" xdt:Transform="Remove" />
</handlers>
<add name="umbracoRequestModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<add name="viewstateMoverModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<add name="umbracoBaseRequestModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<!-- Note, we're removing the one that starts with a space here, don't correct it -->
<!-- This to fix a quirk we for a lot of releases where we added it with the space by default -->
<add name=" UmbracoModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<add name="UmbracoModule" type="Umbraco.Web.UmbracoModule,umbraco" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
</modules>
<security xdt:Transform="InsertIfMissing">
<requestFiltering xdt:Transform="InsertIfMissing">
<requestLimits maxAllowedContentLength="52428800" xdt:Transform="InsertIfMissing" />
</requestFiltering>
</security>
<staticContent xdt:Transform="InsertIfMissing" />
<staticContent>
<remove fileExtension=".svg" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
<mimeMap fileExtension=".svg" mimeType="image/svg+xml" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
<remove fileExtension=".woff" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
<mimeMap fileExtension=".woff" mimeType="application/x-font-woff" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
<remove fileExtension=".woff2" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
<mimeMap fileExtension=".woff2" mimeType="application/x-font-woff2" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
<remove fileExtension=".less" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
<mimeMap fileExtension=".less" mimeType="text/css" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
</staticContent>
</system.webServer>
<handlers>
<remove name="SpellChecker" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<add name="SpellChecker" xdt:Locator="Match(name)" xdt:Transform="Remove" />
</handlers>
<runtime xdt:Transform="InsertIfMissing" />
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1" xdt:Transform="InsertIfMissing" />
</runtime>
<security xdt:Transform="InsertIfMissing">
<requestFiltering xdt:Transform="InsertIfMissing">
<requestLimits maxAllowedContentLength="52428800" xdt:Transform="InsertIfMissing" />
</requestFiltering>
</security>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='HtmlAgilityPack')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='AutoMapper')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Net.Http')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Newtonsoft.Json')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Web.Mvc')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Web.WebPages.Razor')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Web.Http')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Microsoft.Owin')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Microsoft.Owin.Security.OAuth')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Microsoft.Owin.Security')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Microsoft.Owin.Security.Cookies')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Net.Http.Formatting')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Microsoft.CodeAnalysis.CSharp')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='log4net')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Data.SqlServerCe')" xdt:Transform="Remove" />
</assemblyBinding>
</runtime>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin.Security.Cookies" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin.Security.OAuth" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-11.0.0.0" newVersion="11.0.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.2.1.0" newVersion="1.2.1.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.6.0" newVersion="5.2.6.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.6.0" newVersion="5.2.6.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<system.web.webPages.razor xdt:Transform="Remove" />
<location path="umbraco" xdt:Locator="Match(path)" xdt:Transform="InsertIfMissing" />
<location path="umbraco" xdt:Locator="Match(path)">
<system.webServer xdt:Transform="InsertIfMissing">
<urlCompression doStaticCompression="false" doDynamicCompression="false" dynamicCompressionBeforeCache="false" xdt:Transform="SetAttributes(doStaticCompression,doDynamicCompression,dynamicCompressionBeforeCache)" />
</system.webServer>
</location>
<location path="App_Plugins" xdt:Locator="Match(path)" xdt:Transform="InsertIfMissing" />
<location path="App_Plugins" xdt:Locator="Match(path)">
<system.webServer xdt:Transform="InsertIfMissing">
<urlCompression doStaticCompression="false" doDynamicCompression="false" dynamicCompressionBeforeCache="false" xdt:Transform="SetAttributes(doStaticCompression,doDynamicCompression,dynamicCompressionBeforeCache)" />
</system.webServer>
</location>
<runtime xdt:Transform="InsertIfMissing" />
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1" xdt:Transform="InsertIfMissing" />
</runtime>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='HtmlAgilityPack')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='AutoMapper')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Net.Http')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Newtonsoft.Json')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Web.Mvc')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Web.WebPages.Razor')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Web.Http')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Microsoft.Owin')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Microsoft.Owin.Security.OAuth')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Microsoft.Owin.Security')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Microsoft.Owin.Security.Cookies')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Net.Http.Formatting')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Microsoft.CodeAnalysis.CSharp')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='log4net')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Data.SqlServerCe')" xdt:Transform="Remove" />
</assemblyBinding>
</runtime>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="HtmlAgilityPack" publicKeyToken="bd319b19eaf3b43a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.4.9.5" newVersion="1.4.9.5" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="AutoMapper" publicKeyToken="be96cd2c38ef1005" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.3.1.0" newVersion="3.3.1.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0"/>
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Web.WebPages.Razor" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin.Security.OAuth" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin.Security.Cookies" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="log4net" publicKeyToken="669e0ddf0bb1aa2a" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-2.0.8.0" newVersion="2.0.8.0"/>
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Data.SqlServerCe" publicKeyToken="89845DCD8080CC91" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.0.0.1" newVersion="4.0.0.1"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
<system.web.webPages.razor xdt:Transform="Remove" />
<location path="umbraco" xdt:Locator="Match(path)" xdt:Transform="InsertIfMissing" />
<location path="umbraco" xdt:Locator="Match(path)">
<system.webServer xdt:Transform="InsertIfMissing">
<urlCompression doStaticCompression="false" doDynamicCompression="false" dynamicCompressionBeforeCache="false" xdt:Transform="SetAttributes(doStaticCompression,doDynamicCompression,dynamicCompressionBeforeCache)" />
</system.webServer>
</location>
<location path="App_Plugins" xdt:Locator="Match(path)" xdt:Transform="InsertIfMissing" />
<location path="App_Plugins" xdt:Locator="Match(path)">
<system.webServer xdt:Transform="InsertIfMissing">
<urlCompression doStaticCompression="false" doDynamicCompression="false" dynamicCompressionBeforeCache="false" xdt:Transform="SetAttributes(doStaticCompression,doDynamicCompression,dynamicCompressionBeforeCache)" />
</system.webServer>
</location>
</configuration>

View File

@@ -1,11 +1,11 @@
<?xml version="1.0"?>
<applications xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<add alias="content" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon)" />
<add alias="media" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon)" />
<add alias="settings" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon)" />
<add alias="developer" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon)" />
<add alias="users" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon)" />
<add alias="member" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon)" />
<add alias="forms" name="Forms" sortOrder="6" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing" />
<add alias="translation" sortOrder="7" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon,sortOrder)" />
</applications>
<add alias="content" icon="traycontent" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon)" />
<add alias="media" icon="traymedia" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon)" />
<add alias="settings" icon="traysettings" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon)" />
<add alias="developer" icon="traydeveloper" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon)" />
<add alias="users" icon="trayusers" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon)" />
<add alias="member" icon="traymember" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon)" />
<add alias="forms" name="Forms" icon="icon-umb-contour" sortOrder="6" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing" />
<add alias="translation" icon="traytranslation" sortOrder="7" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon,sortOrder)" />
</applications>

View File

@@ -33,15 +33,26 @@ if ($project) {
robocopy $umbracoBinFolder $umbracoBinBackupPath /e /LOG:$copyLogsPath\UmbracoBinBackup.log
# Delete files Umbraco ships with
if(Test-Path $umbracoBinFolder\businesslogic.dll) { Remove-Item $umbracoBinFolder\businesslogic.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\cms.dll) { Remove-Item $umbracoBinFolder\cms.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\controls.dll) { Remove-Item $umbracoBinFolder\controls.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\interfaces.dll) { Remove-Item $umbracoBinFolder\interfaces.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\log4net.dll) { Remove-Item $umbracoBinFolder\log4net.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.ApplicationBlocks.Data.dll) { Remove-Item $umbracoBinFolder\Microsoft.ApplicationBlocks.Data.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\SQLCE4Umbraco.dll) { Remove-Item $umbracoBinFolder\SQLCE4Umbraco.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Data.SqlServerCe.dll) { Remove-Item $umbracoBinFolder\System.Data.SqlServerCe.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Data.SqlServerCe.Entity.dll) { Remove-Item $umbracoBinFolder\System.Data.SqlServerCe.Entity.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Umbraco.Web.dll) { Remove-Item $umbracoBinFolder\Umbraco.Web.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\TidyNet.dll) { Remove-Item $umbracoBinFolder\TidyNet.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\umbraco.dll) { Remove-Item $umbracoBinFolder\umbraco.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Umbraco.Core.dll) { Remove-Item $umbracoBinFolder\Umbraco.Core.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\umbraco.DataLayer.dll) { Remove-Item $umbracoBinFolder\umbraco.DataLayer.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\umbraco.editorControls.dll) { Remove-Item $umbracoBinFolder\umbraco.editorControls.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\umbraco.MacroEngines.dll) { Remove-Item $umbracoBinFolder\umbraco.MacroEngines.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Umbraco.ModelsBuilder.dll) { Remove-Item $umbracoBinFolder\Umbraco.ModelsBuilder.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Umbraco.ModelsBuilder.AspNet.dll) { Remove-Item $umbracoBinFolder\Umbraco.ModelsBuilder.AspNet.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\umbraco.providers.dll) { Remove-Item $umbracoBinFolder\umbraco.providers.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Umbraco.Web.UI.dll) { Remove-Item $umbracoBinFolder\Umbraco.Web.UI.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Umbraco.Examine.dll) { Remove-Item $umbracoBinFolder\Umbraco.Examine.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\UmbracoExamine.dll) { Remove-Item $umbracoBinFolder\UmbracoExamine.dll -Force -Confirm:$false }
# Delete files Umbraco depends upon
$amd64Folder = Join-Path $umbracoBinFolder "amd64"
@@ -91,4 +102,4 @@ if ($project) {
if(Test-Path $umbracoBinFolder\System.Web.WebPages.Deployment.dll) { Remove-Item $umbracoBinFolder\System.Web.WebPages.Deployment.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.WebPages.Razor.dll) { Remove-Item $umbracoBinFolder\System.Web.WebPages.Razor.dll -Force -Confirm:$false }
}
}
}

View File

@@ -1,146 +1,162 @@
param($installPath, $toolsPath, $package, $project)
Write-Host "installPath:" "${installPath}"
Write-Host "toolsPath:" "${toolsPath}"
Write-Host " "
if ($project) {
$dateTime = Get-Date -Format yyyyMMdd-HHmmss
# Create paths and list them
$projectPath = (Get-Item $project.Properties.Item("FullPath").Value).FullName
Write-Host "projectPath:" "${projectPath}"
$backupPath = Join-Path $projectPath "App_Data\NuGetBackup\$dateTime"
Write-Host "backupPath:" "${backupPath}"
$copyLogsPath = Join-Path $backupPath "CopyLogs"
Write-Host "copyLogsPath:" "${copyLogsPath}"
$webConfigSource = Join-Path $projectPath "Web.config"
Write-Host "webConfigSource:" "${webConfigSource}"
$configFolder = Join-Path $projectPath "Config"
Write-Host "configFolder:" "${configFolder}"
# Create backup folder and logs folder if it doesn't exist yet
New-Item -ItemType Directory -Force -Path $backupPath
New-Item -ItemType Directory -Force -Path $copyLogsPath
# Create a backup of original web.config
Copy-Item $webConfigSource $backupPath -Force
# Backup config files folder
if(Test-Path $configFolder) {
$umbracoBackupPath = Join-Path $backupPath "Config"
New-Item -ItemType Directory -Force -Path $umbracoBackupPath
robocopy $configFolder $umbracoBackupPath /e /LOG:$copyLogsPath\ConfigBackup.log
}
# Copy umbraco and umbraco_files from package to project folder
$umbracoFolder = Join-Path $projectPath "Umbraco"
New-Item -ItemType Directory -Force -Path $umbracoFolder
$umbracoFolderSource = Join-Path $installPath "UmbracoFiles\Umbraco"
$umbracoBackupPath = Join-Path $backupPath "Umbraco"
New-Item -ItemType Directory -Force -Path $umbracoBackupPath
robocopy $umbracoFolder $umbracoBackupPath /e /LOG:$copyLogsPath\UmbracoBackup.log
robocopy $umbracoFolderSource $umbracoFolder /is /it /e /xf UI.xml /LOG:$copyLogsPath\UmbracoCopy.log
$copyWebconfig = $true
$destinationWebConfig = Join-Path $projectPath "Web.config"
if(Test-Path $destinationWebConfig)
{
Try
{
[xml]$config = Get-Content $destinationWebConfig
$config.configuration.appSettings.ChildNodes | ForEach-Object {
if($_.key -eq "umbracoConfigurationStatus")
{
# The web.config has an umbraco-specific appSetting in it
# don't overwrite it and let config transforms do their thing
$copyWebconfig = $false
}
}
}
Catch { }
}
if($copyWebconfig -eq $true)
{
$packageWebConfigSource = Join-Path $installPath "UmbracoFiles\Web.config"
Copy-Item $packageWebConfigSource $destinationWebConfig -Force
# Copy files that don't get automatically copied for Website projects
# We do this here, when copyWebconfig is true because we only want to do it for new installs
# If this is an upgrade then the files should already be there
$splashesSource = Join-Path $installPath "UmbracoFiles\Config\splashes\*.*"
$splashesDestination = Join-Path $projectPath "Config\splashes\"
New-Item $splashesDestination -Type directory
Copy-Item $splashesSource $splashesDestination -Force
$umbracoUIXMLSource = Join-Path $installPath "UmbracoFiles\Umbraco\Config\Create\UI.xml"
$umbracoUIXMLDestination = Join-Path $projectPath "Umbraco\Config\Create\UI.xml"
Copy-Item $umbracoUIXMLSource $umbracoUIXMLDestination -Force
} else {
# This part only runs for upgrades
$upgradeViewSource = Join-Path $umbracoFolderSource "Views\install\*"
$upgradeView = Join-Path $umbracoFolder "Views\install\"
Write-Host "Copying2 ${upgradeViewSource} to ${upgradeView}"
Copy-Item $upgradeViewSource $upgradeView -Force
Try
{
# Disable tours for upgrades, presumably Umbraco experience is already available
$umbracoSettingsConfigPath = Join-Path $configFolder "umbracoSettings.config"
$content = (Get-Content $umbracoSettingsConfigPath).Replace('<tours enable="true">','<tours enable="false">')
# Saves with UTF-8 encoding without BOM which makes sure Umbraco can still read it
# Reference: https://stackoverflow.com/a/32951824/5018
[IO.File]::WriteAllLines($umbracoSettingsConfigPath, $content)
}
Catch
{
# Not a big problem if this fails, let it go
}
Try
{
$uiXmlConfigPath = Join-Path $umbracoFolder -ChildPath "Config" | Join-Path -ChildPath "create" | Join-Path -ChildPath "UI.xml"
$uiXmlFile = Join-Path $umbracoFolder -ChildPath "Config" | Join-Path -ChildPath "create" | Join-Path -ChildPath "UI.xml"
$uiXml = New-Object System.Xml.XmlDocument
$uiXml.PreserveWhitespace = $true
$uiXml.Load($uiXmlFile)
$createExists = $uiXml.SelectNodes("//nodeType[@alias='macros']/tasks/create")
if($createExists.Count -eq 0)
{
$macrosTasksNode = $uiXml.SelectNodes("//nodeType[@alias='macros']/tasks")
#Creating: <create assembly="umbraco" type="macroTasks" />
$createNode = $uiXml.CreateElement("create")
$createNode.SetAttribute("assembly", "umbraco")
$createNode.SetAttribute("type", "macroTasks")
$macrosTasksNode.AppendChild($createNode)
$uiXml.Save($uiXmlFile)
}
}
Catch { }
}
$installFolder = Join-Path $projectPath "Install"
if(Test-Path $installFolder) {
Remove-Item $installFolder -Force -Recurse -Confirm:$false
}
# Open appropriate readme
if($copyWebconfig -eq $true)
{
$DTE.ItemOperations.OpenFile($toolsPath + '\Readme.txt')
}
else
{
$DTE.ItemOperations.OpenFile($toolsPath + '\ReadmeUpgrade.txt')
}
}
param($installPath, $toolsPath, $package, $project)
Write-Host "installPath:" "${installPath}"
Write-Host "toolsPath:" "${toolsPath}"
Write-Host " "
if ($project) {
$dateTime = Get-Date -Format yyyyMMdd-HHmmss
# Create paths and list them
$projectPath = (Get-Item $project.Properties.Item("FullPath").Value).FullName
Write-Host "projectPath:" "${projectPath}"
$backupPath = Join-Path $projectPath "App_Data\NuGetBackup\$dateTime"
Write-Host "backupPath:" "${backupPath}"
$copyLogsPath = Join-Path $backupPath "CopyLogs"
Write-Host "copyLogsPath:" "${copyLogsPath}"
$webConfigSource = Join-Path $projectPath "Web.config"
Write-Host "webConfigSource:" "${webConfigSource}"
$configFolder = Join-Path $projectPath "Config"
Write-Host "configFolder:" "${configFolder}"
# Create backup folder and logs folder if it doesn't exist yet
New-Item -ItemType Directory -Force -Path $backupPath
New-Item -ItemType Directory -Force -Path $copyLogsPath
# Create a backup of original web.config
Copy-Item $webConfigSource $backupPath -Force
# Backup config files folder
if(Test-Path $configFolder) {
$umbracoBackupPath = Join-Path $backupPath "Config"
New-Item -ItemType Directory -Force -Path $umbracoBackupPath
robocopy $configFolder $umbracoBackupPath /e /LOG:$copyLogsPath\ConfigBackup.log
}
# Copy umbraco and umbraco_files from package to project folder
$umbracoFolder = Join-Path $projectPath "Umbraco"
New-Item -ItemType Directory -Force -Path $umbracoFolder
$umbracoFolderSource = Join-Path $installPath "UmbracoFiles\Umbraco"
$umbracoBackupPath = Join-Path $backupPath "Umbraco"
New-Item -ItemType Directory -Force -Path $umbracoBackupPath
robocopy $umbracoFolder $umbracoBackupPath /e /LOG:$copyLogsPath\UmbracoBackup.log
robocopy $umbracoFolderSource $umbracoFolder /is /it /e /xf UI.xml /LOG:$copyLogsPath\UmbracoCopy.log
$umbracoClientFolder = Join-Path $projectPath "Umbraco_Client"
New-Item -ItemType Directory -Force -Path $umbracoClientFolder
$umbracoClientFolderSource = Join-Path $installPath "UmbracoFiles\Umbraco_Client"
$umbracoClientBackupPath = Join-Path $backupPath "Umbraco_Client"
New-Item -ItemType Directory -Force -Path $umbracoClientBackupPath
robocopy $umbracoClientFolder $umbracoClientBackupPath /e /LOG:$copyLogsPath\UmbracoClientBackup.log
robocopy $umbracoClientFolderSource $umbracoClientFolder /is /it /e /LOG:$copyLogsPath\UmbracoClientCopy.log
$copyWebconfig = $true
$destinationWebConfig = Join-Path $projectPath "Web.config"
if(Test-Path $destinationWebConfig)
{
Try
{
[xml]$config = Get-Content $destinationWebConfig
$config.configuration.appSettings.ChildNodes | ForEach-Object {
if($_.key -eq "umbracoConfigurationStatus")
{
# The web.config has an umbraco-specific appSetting in it
# don't overwrite it and let config transforms do their thing
$copyWebconfig = $false
}
}
}
Catch { }
}
if($copyWebconfig -eq $true)
{
$packageWebConfigSource = Join-Path $installPath "UmbracoFiles\Web.config"
Copy-Item $packageWebConfigSource $destinationWebConfig -Force
# Copy files that don't get automatically copied for Website projects
# We do this here, when copyWebconfig is true because we only want to do it for new installs
# If this is an upgrade then the files should already be there
$splashesSource = Join-Path $installPath "UmbracoFiles\Config\splashes\*.*"
$splashesDestination = Join-Path $projectPath "Config\splashes\"
New-Item $splashesDestination -Type directory
Copy-Item $splashesSource $splashesDestination -Force
$sqlCe64Source = Join-Path $installPath "UmbracoFiles\bin\amd64\*"
$sqlCe64Destination = Join-Path $projectPath "bin\amd64\"
Copy-Item $sqlCe64Source $sqlCe64Destination -Force
$sqlCex86Source = Join-Path $installPath "UmbracoFiles\bin\x86\*"
$sqlCex86Destination = Join-Path $projectPath "bin\x86\"
Copy-Item $sqlCex86source $sqlCex86Destination -Force
$umbracoUIXMLSource = Join-Path $installPath "UmbracoFiles\Umbraco\Config\Create\UI.xml"
$umbracoUIXMLDestination = Join-Path $projectPath "Umbraco\Config\Create\UI.xml"
Copy-Item $umbracoUIXMLSource $umbracoUIXMLDestination -Force
} else {
# This part only runs for upgrades
$upgradeViewSource = Join-Path $umbracoFolderSource "Views\install\*"
$upgradeView = Join-Path $umbracoFolder "Views\install\"
Write-Host "Copying2 ${upgradeViewSource} to ${upgradeView}"
Copy-Item $upgradeViewSource $upgradeView -Force
Try
{
# Disable tours for upgrades, presumably Umbraco experience is already available
$umbracoSettingsConfigPath = Join-Path $configFolder "umbracoSettings.config"
$content = (Get-Content $umbracoSettingsConfigPath).Replace('<tours enable="true">','<tours enable="false">')
# Saves with UTF-8 encoding without BOM which makes sure Umbraco can still read it
# Reference: https://stackoverflow.com/a/32951824/5018
[IO.File]::WriteAllLines($umbracoSettingsConfigPath, $content)
}
Catch
{
# Not a big problem if this fails, let it go
}
Try
{
$uiXmlConfigPath = Join-Path $umbracoFolder -ChildPath "Config" | Join-Path -ChildPath "create" | Join-Path -ChildPath "UI.xml"
$uiXmlFile = Join-Path $umbracoFolder -ChildPath "Config" | Join-Path -ChildPath "create" | Join-Path -ChildPath "UI.xml"
$uiXml = New-Object System.Xml.XmlDocument
$uiXml.PreserveWhitespace = $true
$uiXml.Load($uiXmlFile)
$createExists = $uiXml.SelectNodes("//nodeType[@alias='macros']/tasks/create")
if($createExists.Count -eq 0)
{
$macrosTasksNode = $uiXml.SelectNodes("//nodeType[@alias='macros']/tasks")
#Creating: <create assembly="umbraco" type="macroTasks" />
$createNode = $uiXml.CreateElement("create")
$createNode.SetAttribute("assembly", "umbraco")
$createNode.SetAttribute("type", "macroTasks")
$macrosTasksNode.AppendChild($createNode)
$uiXml.Save($uiXmlFile)
}
}
Catch { }
}
$installFolder = Join-Path $projectPath "Install"
if(Test-Path $installFolder) {
Remove-Item $installFolder -Force -Recurse -Confirm:$false
}
# Open appropriate readme
if($copyWebconfig -eq $true)
{
$DTE.ItemOperations.OpenFile($toolsPath + '\Readme.txt')
}
else
{
$DTE.ItemOperations.OpenFile($toolsPath + '\ReadmeUpgrade.txt')
}
}

View File

@@ -4,18 +4,18 @@
<add alias="content" application="content"
xdt:Locator="Match(application,alias)"
xdt:Transform="RemoveAttributes(silent)" />
<add initialize="false" sortOrder="0" alias="contentRecycleBin" application="content" title="Recycle Bin" iconClosed="icon-folder" iconOpen="icon-folder" type="umbraco.cms.presentation.Trees.ContentRecycleBin, Umbraco.Web"
<add initialize="false" sortOrder="0" alias="contentRecycleBin" application="content" title="Recycle Bin" iconClosed="icon-folder" iconOpen="icon-folder" type="umbraco.cms.presentation.Trees.ContentRecycleBin, umbraco"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add initialize="true" sortOrder="0" alias="content" application="content" title="Content" iconClosed="icon-folder" iconOpen="icon-folder" type="Umbraco.Web.Trees.ContentTreeController, Umbraco.Web"
<add initialize="true" sortOrder="0" alias="content" application="content" title="Content" iconClosed="icon-folder" iconOpen="icon-folder" type="Umbraco.Web.Trees.ContentTreeController, umbraco"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<!--Media-->
<add initialize="true" sortOrder="0" alias="media" application="media" title="Media" iconClosed="icon-folder" iconOpen="icon-folder" type="Umbraco.Web.Trees.MediaTreeController, Umbraco.Web"
<add initialize="true" sortOrder="0" alias="media" application="media" title="Media" iconClosed="icon-folder" iconOpen="icon-folder" type="Umbraco.Web.Trees.MediaTreeController, umbraco"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add initialize="false" sortOrder="0" alias="mediaRecycleBin" application="media" title="Recycle Bin" iconClosed="icon-folder" iconOpen="icon-folder" type="umbraco.cms.presentation.Trees.MediaRecycleBin, Umbraco.Web"
<add initialize="false" sortOrder="0" alias="mediaRecycleBin" application="media" title="Recycle Bin" iconClosed="icon-folder" iconOpen="icon-folder" type="umbraco.cms.presentation.Trees.MediaRecycleBin, umbraco"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
@@ -23,39 +23,39 @@
<add application="settings" alias="nodeTypes"
xdt:Locator="Match(application,alias)"
xdt:Transform="Remove" />
<add initialize="true" sortOrder="0" alias="documentTypes" application="settings" title="Document Types" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.ContentTypeTreeController, Umbraco.Web"
<add initialize="true" sortOrder="0" alias="documentTypes" application="settings" title="Document Types" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.ContentTypeTreeController, umbraco"
xdt:Locator="Match(application,alias)"
xdt:Transform="InsertIfMissing" />
<add application="settings" alias="stylesheets" title="Stylesheets" type="umbraco.loadStylesheets, Umbraco.Web" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="3"
<add application="settings" alias="stylesheets" title="Stylesheets" type="umbraco.loadStylesheets, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="3"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="settings" alias="stylesheetProperty" title="Stylesheet Property" type="umbraco.loadStylesheetProperty, Umbraco.Web" iconClosed="" iconOpen="" initialize="false" sortOrder="0"
<add application="settings" alias="stylesheetProperty" title="Stylesheet Property" type="umbraco.loadStylesheetProperty, umbraco" iconClosed="" iconOpen="" initialize="false" sortOrder="0"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="settings" alias="templates" title="Templates" type="Umbraco.Web.Trees.TemplatesTreeController, Umbraco.Web" iconClosed="icon-folder" iconOpen="icon-folder-open" sortOrder="1"
<add application="settings" alias="templates" title="Templates" type="Umbraco.Web.Trees.TemplatesTreeController, umbraco" iconClosed="icon-folder" iconOpen="icon-folder-open" sortOrder="1"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="settings" alias="partialViews" type="Umbraco.Web.Trees.PartialViewsTree, Umbraco.Web"
<add application="settings" alias="partialViews" type="Umbraco.Web.Trees.PartialViewsTree, umbraco"
xdt:Locator="Match(application,alias,type)"
xdt:Transform="Remove()" />
<add application="settings" alias="partialViews" title="Partial Views" silent="false" initialize="true" iconClosed="icon-folder" iconOpen="icon-folder" type="Umbraco.Web.Trees.PartialViewsTreeController, Umbraco.Web" sortOrder="2"
<add application="settings" alias="partialViews" title="Partial Views" silent="false" initialize="true" iconClosed="icon-folder" iconOpen="icon-folder" type="Umbraco.Web.Trees.PartialViewsTreeController, umbraco" sortOrder="2"
xdt:Locator="Match(application,alias)"
xdt:Transform="InsertIfMissing" />
<add application="settings" alias="scripts" title="Scripts" type="Umbraco.Web.Trees.ScriptTreeController, Umbraco.Web" 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.Web.Trees.DictionaryTreeController, Umbraco.Web" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="8"
<add application="settings" alias="dictionary" title="Dictionary" type="Umbraco.Web.Trees.DictionaryTreeController, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="6"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add alias="dictionary" application="settings"
xdt:Locator="Match(application,alias)"
xdt:Transform="RemoveAttributes(action)" />
<add application="settings" alias="languages" title="Languages" type="Umbraco.Web.Trees.LanguageTreeController, Umbraco.Web" iconClosed="icon-folder" iconOpen="icon-folder-open" sortOrder="5"
<add application="settings" alias="languages" title="Languages" type="Umbraco.Web.Trees.LanguageTreeController, umbraco" iconClosed="icon-folder" iconOpen="icon-folder-open" sortOrder="5"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="settings" initialize="true" alias="mediaTypes" title="Media Types" type="Umbraco.Web.Trees.MediaTypeTreeController, Umbraco.Web" iconClosed="icon-folder" iconOpen="icon-folder-open" sortOrder="7"
<add application="settings" initialize="true" alias="mediaTypes" title="Media Types" type="Umbraco.Web.Trees.MediaTypeTreeController, umbraco" iconClosed="icon-folder" iconOpen="icon-folder-open" sortOrder="7"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
@@ -66,12 +66,12 @@
<add alias="packagerPackages" application="developer"
xdt:Locator="Match(application,alias)"
xdt:Transform="Remove" />
<add initialize="true" sortOrder="0" alias="packager" application="developer" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.PackagesTreeController, Umbraco.Web"
<add initialize="true" sortOrder="0" alias="packager" application="developer" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.PackagesTreeController, umbraco"
xdt:Locator="Match(application,alias)"
xdt:Transform="InsertIfMissing" />
<!-- Before 7.4 this tree had the alias 'dataType', without the 's' on the end, this is here to rename it -->
<add sortOrder="1" alias="dataTypes" application="developer" type="Umbraco.Web.Trees.DataTypeTreeController, Umbraco.Web"
<add sortOrder="1" alias="dataTypes" application="developer" type="Umbraco.Web.Trees.DataTypeTreeController, umbraco"
xdt:Locator="Match(application,type)"
xdt:Transform="SetAttributes(alias,sortOrder)" />
@@ -80,17 +80,20 @@
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes(sortOrder)" />
<add initialize="true" sortOrder="2" alias="macros" application="developer" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.MacroTreeController, Umbraco.Web"
<add initialize="true" sortOrder="2" alias="macros" application="developer" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.MacroTreeController, umbraco"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="developer" alias="relationTypes" title="Relation Types" type="umbraco.loadRelationTypes, Umbraco.Web" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="4"
<add application="developer" alias="relationTypes" title="Relation Types" type="umbraco.loadRelationTypes, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="4"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add initialize="true" sortOrder="5" alias="xslt" application="developer" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.XsltTreeController, umbraco"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="developer" alias="partialViewMacros" type="Umbraco.Web.Trees.PartialViewMacrosTree, Umbraco.Web"
<add application="developer" alias="partialViewMacros" type="Umbraco.Web.Trees.PartialViewMacrosTree, umbraco"
xdt:Locator="Match(application,alias,type)"
xdt:Transform="Remove()" />
<add application="developer" alias="partialViewMacros" type="Umbraco.Web.Trees.PartialViewMacrosTreeController, Umbraco.Web" silent="false" initialize="true" sortOrder="6" title="Partial View Macro Files" iconClosed="icon-folder" iconOpen="icon-folder"
<add application="developer" alias="partialViewMacros" type="Umbraco.Web.Trees.PartialViewMacrosTreeController, umbraco" silent="false" initialize="true" sortOrder="6" title="Partial View Macro Files" iconClosed="icon-folder" iconOpen="icon-folder"
xdt:Locator="Match(application,alias)"
xdt:Transform="InsertIfMissing" />
@@ -99,7 +102,7 @@
xdt:Transform="Remove" />
<!--Users-->
<add initialize="true" sortOrder="0" alias="users" application="users" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.UserTreeController, Umbraco.Web"
<add initialize="true" sortOrder="0" alias="users" application="users" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.UserTreeController, umbraco"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
@@ -111,30 +114,30 @@
xdt:Transform="Remove" />
<!--Members-->
<add initialize="true" sortOrder="0" alias="member" application="member" title="Members" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.MemberTreeController, Umbraco.Web"
<add initialize="true" sortOrder="0" alias="member" application="member" title="Members" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.MemberTreeController, umbraco"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="member" alias="memberGroup"
xdt:Locator="Match(application,alias)"
xdt:Transform="Remove" />
<add application="member" sortOrder="2" alias="memberGroups" title="Member Groups" type="umbraco.loadMemberGroups, Umbraco.Web" iconClosed="icon-folder" iconOpen="icon-folder"
<add application="member" sortOrder="2" alias="memberGroups" title="Member Groups" type="umbraco.loadMemberGroups, umbraco" iconClosed="icon-folder" iconOpen="icon-folder"
xdt:Locator="Match(application,alias)"
xdt:Transform="InsertIfMissing" />
<add application="member" alias="memberType"
xdt:Locator="Match(application,alias)"
xdt:Transform="Remove" />
<add application="member" sortOrder="1" alias="memberTypes" initialize="true" title="Member Types" type="Umbraco.Web.Trees.MemberTypeTreeController, Umbraco.Web" iconClosed="icon-folder" iconOpen="icon-folder-open"
<add application="member" sortOrder="1" alias="memberTypes" initialize="true" title="Member Types" type="Umbraco.Web.Trees.MemberTypeTreeController, umbraco" iconClosed="icon-folder" iconOpen="icon-folder-open"
xdt:Locator="Match(application,alias)"
xdt:Transform="InsertIfMissing" />
<!--Translation-->
<add silent="false" initialize="true" sortOrder="1" alias="openTasks" application="translation" title="Tasks assigned to you" iconClosed="icon-folder" iconOpen="icon-folder" type="umbraco.loadOpenTasks, Umbraco.Web"
<add silent="false" initialize="true" sortOrder="1" alias="openTasks" application="translation" title="Tasks assigned to you" iconClosed="icon-folder" iconOpen="icon-folder" type="umbraco.loadOpenTasks, umbraco"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add alias="openTasks" application="translation"
xdt:Locator="Match(application,alias)"
xdt:Transform="RemoveAttributes(action)" />
<add silent="false" initialize="true" sortOrder="2" alias="yourTasks" application="translation" title="Tasks created by you" iconClosed="icon-folder" iconOpen="icon-folder" type="umbraco.loadYourTasks, Umbraco.Web"
<add silent="false" initialize="true" sortOrder="2" alias="yourTasks" application="translation" title="Tasks created by you" iconClosed="icon-folder" iconOpen="icon-folder" type="umbraco.loadYourTasks, umbraco"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add alias="yourTasks" application="translation"

View File

@@ -1,92 +0,0 @@
# this script should be dot-sourced into the build.ps1 scripts
# right after the parameters declaration
# ie
# . "$PSScriptRoot\build-bootstrap.ps1"
# THIS FILE IS DISTRIBUTED AS PART OF UMBRACO.BUILD
# DO NOT MODIFY IT - ALWAYS USED THE COMMON VERSION
# ################################################################
# BOOTSTRAP
# ################################################################
# reset errors
$error.Clear()
# ensure we have temp folder for downloads
$scriptRoot = "$PSScriptRoot"
$scriptTemp = "$scriptRoot\temp"
if (-not (test-path $scriptTemp)) { mkdir $scriptTemp > $null }
# get NuGet
$cache = 4
$nuget = "$scriptTemp\nuget.exe"
if (-not $local)
{
$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-Item $nuget -force -errorAction SilentlyContinue > $null
}
if (-not (test-path $nuget))
{
Write-Host "Download NuGet..."
Invoke-WebRequest $source -OutFile $nuget
if (-not $?) { throw "Failed to download NuGet." }
}
}
elseif (-not (test-path $nuget))
{
throw "Failed to locate NuGet.exe."
}
# NuGet notes
# As soon as we use -ConfigFile, NuGet uses that file, and only that file, and does not
# merge configuration from system level. See comments in NuGet.Client solution, class
# NuGet.Configuration.Settings, method LoadDefaultSettings.
# For NuGet to merge configurations, it needs to "find" the file in the current directory,
# or above. Which means we cannot really use -ConfigFile but instead have to have Umbraco's
# NuGet.config file at root, and always run NuGet.exe while at root or in a directory below
# root.
$solutionRoot = "$scriptRoot\.."
$testPwd = [System.IO.Path]::GetFullPath($pwd.Path) + "\"
$testRoot = [System.IO.Path]::GetFullPath($solutionRoot) + "\"
if (-not $testPwd.ToLower().StartsWith($testRoot.ToLower()))
{
throw "Cannot run outside of the solution's root."
}
# get the build system
if (-not $local)
{
$params = "-OutputDirectory", $scriptTemp, "-Verbosity", "quiet", "-PreRelease"
&$nuget install Umbraco.Build @params
if (-not $?) { throw "Failed to download Umbraco.Build." }
}
# ensure we have the build system
$ubuildPath = ls "$scriptTemp\Umbraco.Build.*" | sort -property CreationTime -descending | select -first 1
if (-not $ubuildPath)
{
throw "Failed to locate the build system."
}
# boot the build system
# this creates $global:ubuild
return &"$ubuildPath\ps\Boot.ps1"
# at that point the build.ps1 script must boot the build system
# eg
# $ubuild.Boot($ubuildPath.FullName, [System.IO.Path]::GetFullPath("$scriptRoot\.."),
# @{ Local = $local; With7Zip = $false; WithNode = $false },
# @{ continue = $continue })
# if (-not $?) { throw "Failed to boot the build system." }
#
# and it's good practice to report
# eg
# Write-Host "Umbraco.Whatever Build"
# Write-Host "Umbraco.Build v$($ubuild.BuildVersion)"
# eof

View File

@@ -1,475 +1,67 @@
param (
# get, don't execute
param (
[Parameter(Mandatory=$false)]
[Alias("g")]
[switch] $get = $false,
[string]
$version,
# run local, don't download, assume everything is ready
[Parameter(Mandatory=$false)]
[Alias("l")]
[Alias("loc")]
[switch] $local = $false,
[Alias("mo")]
[switch]
$moduleOnly = $false
)
# keep the build directories, don't clear them
[Parameter(Mandatory=$false)]
[Alias("c")]
[Alias("cont")]
[switch] $continue = $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\"
}
# ################################################################
# BOOTSTRAP
# ################################################################
# 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
}
# create and boot the buildsystem
$ubuild = &"$PSScriptRoot\build-bootstrap.ps1"
if (-not $?) { return }
$ubuild.Boot($PSScriptRoot,
@{ Local = $local; },
@{ Continue = $continue })
if ($ubuild.OnError()) { return }
# add the module path (if not already there)
if (-not $env:PSModulePath.Contains($mpath))
{
$env:PSModulePath = "$mpath;$env:PSModulePath"
}
Write-Host "Umbraco Cms Build"
Write-Host "Umbraco.Build v$($ubuild.BuildVersion)"
# force-import (or re-import) the module
Write-Host "Import Umbraco build Powershell module"
Import-Module Umbraco.Build -Force -DisableNameChecking
# ################################################################
# TASKS
# ################################################################
$ubuild.DefineMethod("SetMoreUmbracoVersion",
# module only?
if ($moduleOnly)
{
if (-not [string]::IsNullOrWhiteSpace($version))
{
param ( $semver )
$release = "" + $semver.Major + "." + $semver.Minor + "." + $semver.Patch
Write-Host "Update IIS Express port in csproj"
$updater = New-Object "Umbraco.Build.ExpressPortUpdater"
$csproj = "$($this.SolutionRoot)\src\Umbraco.Web.UI\Umbraco.Web.UI.csproj"
$updater.Update($csproj, $release)
})
$ubuild.DefineMethod("SandboxNode",
{
$global:node_path = $env:path
$nodePath = $this.BuildEnv.NodePath
$gitExe = (Get-Command git).Source
if (-not $gitExe) { $gitExe = (Get-Command git).Path }
$gitPath = [System.IO.Path]::GetDirectoryName($gitExe)
$env:path = "$nodePath;$gitPath"
$global:node_nodepath = $this.ClearEnvVar("NODEPATH")
$global:node_npmcache = $this.ClearEnvVar("NPM_CONFIG_CACHE")
$global:node_npmprefix = $this.ClearEnvVar("NPM_CONFIG_PREFIX")
# https://github.com/gruntjs/grunt-contrib-connect/issues/235
$this.SetEnvVar("NODE_NO_HTTP2", "1")
})
$ubuild.DefineMethod("RestoreNode",
{
$env:path = $node_path
$this.SetEnvVar("NODEPATH", $node_nodepath)
$this.SetEnvVar("NPM_CONFIG_CACHE", $node_npmcache)
$this.SetEnvVar("NPM_CONFIG_PREFIX", $node_npmprefix)
$ignore = $this.ClearEnvVar("NODE_NO_HTTP2")
})
$ubuild.DefineMethod("CompileBelle",
{
$src = "$($this.SolutionRoot)\src"
$log = "$($this.BuildTemp)\belle.log"
Write-Host "Compile Belle"
Write-Host "Logging to $log"
# get a temp clean node env (will restore)
$this.SandboxNode()
# stupid PS is going to gather all "warnings" in $error
# so we have to take care of it else they'll bubble and kill the build
if ($error.Count -gt 0) { return }
Push-Location "$($this.SolutionRoot)\src\Umbraco.Web.UI.Client"
Write-Output "" > $log
Write-Output "### node version is:" > $log
&node -v >> $log 2>&1
if (-not $?) { throw "Failed to report node version." }
Write-Output "### npm version is:" >> $log 2>&1
&npm -v >> $log 2>&1
if (-not $?) { throw "Failed to report npm version." }
Write-Output "### clean npm cache" >> $log 2>&1
&npm cache clean --force >> $log 2>&1
$error.Clear() # that one can fail 'cos security bug - ignore
Write-Output "### npm install" >> $log 2>&1
&npm install >> $log 2>&1
Write-Output ">> $? $($error.Count)" >> $log 2>&1
Write-Output "### install gulp" >> $log 2>&1
&npm install -g gulp >> $log 2>&1
$error.Clear() # that one fails 'cos deprecated stuff - ignore
Write-Output "### install gulp-cli" >> $log 2>&1
&npm install -g gulp-cli --quiet >> $log 2>&1
$error.Clear() # that one fails 'cos some files not being removed - ignore
Write-Output "### gulp build for version $($this.Version.Release)" >> $log 2>&1
&gulp build --buildversion=$this.Version.Release >> $log 2>&1
if (-not $?) { throw "Failed to build" } # that one is expected to work
Pop-Location
# fixme - should we filter the log to find errors?
#get-content .\build.tmp\belle.log | %{ if ($_ -match "build") { write $_}}
# restore
$this.RestoreNode()
# 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-Host "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)
})
$ubuild.DefineMethod("CompileUmbraco",
{
$buildConfiguration = "Release"
$src = "$($this.SolutionRoot)\src"
$log = "$($this.BuildTemp)\msbuild.umbraco.log"
if ($this.BuildEnv.VisualStudio -eq $null)
{
throw "Build environment does not provide VisualStudio."
}
Write-Host "Compile Umbraco"
Write-Host "Logging to $log"
# beware of the weird double \\ at the end of paths
# see http://edgylogic.com/blog/powershell-and-external-commands-done-right/
&$this.BuildEnv.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="$($this.BuildTemp)\bin\\" `
/p:WebProjectOutputDir="$($this.BuildTemp)\WebApp\\" `
/p:Verbosity=minimal `
/t:Clean`;Rebuild `
/tv:"$($this.BuildEnv.VisualStudio.ToolsVersion)" `
/p:UmbracoBuild=True `
> $log
if (-not $?) { throw "Failed to compile Umbraco.Web.UI." }
# /p:UmbracoBuild tells the csproj that we are building from PS, not VS
})
$ubuild.DefineMethod("PrepareTests",
{
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 "$($this.BuildTemp)\tests\Packaging" ))
{
Write-Host "Create packaging directory"
mkdir "$($this.BuildTemp)\tests\Packaging" > $null
}
$this.CopyFiles("$($this.SolutionRoot)\src\Umbraco.Tests\Packaging\Packages", "*", "$($this.BuildTemp)\tests\Packaging\Packages")
# required for package install tests
if (-not (Test-Path -Path "$($this.BuildTemp)\tests\bin" ))
{
Write-Host "Create bin directory"
mkdir "$($this.BuildTemp)\tests\bin" > $null
}
})
$ubuild.DefineMethod("CompileTests",
{
$buildConfiguration = "Release"
$log = "$($this.BuildTemp)\msbuild.tests.log"
if ($this.BuildEnv.VisualStudio -eq $null)
{
throw "Build environment does not provide VisualStudio."
}
Write-Host "Compile Tests"
Write-Host "Logging to $log"
# beware of the weird double \\ at the end of paths
# see http://edgylogic.com/blog/powershell-and-external-commands-done-right/
&$this.BuildEnv.VisualStudio.MsBuild "$($this.SolutionRoot)\src\Umbraco.Tests\Umbraco.Tests.csproj" `
/p:WarningLevel=0 `
/p:Configuration=$buildConfiguration `
/p:Platform=AnyCPU `
/p:UseWPP_CopyWebApplication=True `
/p:PipelineDependsOnBuild=False `
/p:OutDir="$($this.BuildTemp)\tests\\" `
/p:Verbosity=minimal `
/t:Build `
/tv:"$($this.BuildEnv.VisualStudio.ToolsVersion)" `
/p:UmbracoBuild=True `
> $log
if (-not $?) { throw "Failed to compile tests." }
# /p:UmbracoBuild tells the csproj that we are building from PS
})
$ubuild.DefineMethod("PreparePackages",
{
Write-Host "Prepare Packages"
$src = "$($this.SolutionRoot)\src"
$tmp = "$($this.BuildTemp)"
$out = "$($this.BuildOutput)"
$buildConfiguration = "Release"
# restore web.config
$this.TempRestoreFile("$src\Umbraco.Web.UI\web.config")
# cleanup build
Write-Host "Clean build"
$this.RemoveFile("$tmp\bin\*.dll.config")
$this.RemoveFile("$tmp\WebApp\bin\*.dll.config")
# cleanup presentation
Write-Host "Cleanup presentation"
$this.RemoveDirectory("$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"
Copy-Item -force "$tmp\bin\*.xml" "$tmp\WebApp\bin"
Write-Host "Copy transformed configs and langs"
# note: exclude imageprocessor/*.config as imageprocessor pkg installs them
$this.CopyFiles("$tmp\WebApp\config", "*.config", "$tmp\Configs", `
{ -not $_.RelativeName.StartsWith("imageprocessor") })
$this.CopyFiles("$tmp\WebApp\config", "*.js", "$tmp\Configs")
$this.CopyFiles("$tmp\WebApp\config\lang", "*.xml", "$tmp\Configs\Lang")
$this.CopyFile("$tmp\WebApp\web.config", "$tmp\Configs\web.config.transform")
Write-Host "Copy transformed web.config"
$this.CopyFile("$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"
Get-ChildItem -r "$tmp\*.dll" | ForEach-Object {
$_.CreationTime = $_.CreationTime.AddHours(-11)
$_.LastWriteTime = $_.LastWriteTime.AddHours(-11)
}
# copy libs
Write-Host "Copy SqlCE libraries"
$nugetPackages = $env:NUGET_PACKAGES
if (-not $nugetPackages)
{
$nugetPackages = [System.Environment]::ExpandEnvironmentVariables("%userprofile%\.nuget\packages")
}
$this.CopyFiles("$nugetPackages\umbraco.sqlserverce\4.0.0.1\runtimes\win-x86\native", "*.*", "$tmp\bin\x86")
$this.CopyFiles("$nugetPackages\umbraco.sqlserverce\4.0.0.1\runtimes\win-x64\native", "*.*", "$tmp\bin\amd64")
$this.CopyFiles("$nugetPackages\umbraco.sqlserverce\4.0.0.1\runtimes\win-x86\native", "*.*", "$tmp\WebApp\bin\x86")
$this.CopyFiles("$nugetPackages\umbraco.sqlserverce\4.0.0.1\runtimes\win-x64\native", "*.*", "$tmp\WebApp\bin\amd64")
# copy Belle
Write-Host "Copy Belle"
$this.CopyFiles("$src\Umbraco.Web.UI\umbraco\assets", "*", "$tmp\WebApp\umbraco\assets")
$this.CopyFiles("$src\Umbraco.Web.UI\umbraco\js", "*", "$tmp\WebApp\umbraco\js")
$this.CopyFiles("$src\Umbraco.Web.UI\umbraco\lib", "*", "$tmp\WebApp\umbraco\lib")
$this.CopyFiles("$src\Umbraco.Web.UI\umbraco\views", "*", "$tmp\WebApp\umbraco\views")
})
$ubuild.DefineMethod("PackageZip",
{
Write-Host "Create Zip packages"
$src = "$($this.SolutionRoot)\src"
$tmp = $this.BuildTemp
$out = $this.BuildOutput
Write-Host "Zip all binaries"
&$this.BuildEnv.Zip a -r "$out\UmbracoCms.AllBinaries.$($this.Version.Semver).zip" `
"$tmp\bin\*" `
"-x!dotless.Core.*" `
> $null
if (-not $?) { throw "Failed to zip UmbracoCms.AllBinaries." }
Write-Host "Zip cms"
&$this.BuildEnv.Zip a -r "$out\UmbracoCms.$($this.Version.Semver).zip" `
"$tmp\WebApp\*" `
"-x!dotless.Core.*" "-x!Content_Types.xml" "-x!*.pdb" `
> $null
if (-not $?) { throw "Failed to zip UmbracoCms." }
})
$ubuild.DefineMethod("PrepareBuild",
{
Write-Host "Clear folders and files"
$this.RemoveDirectory("$($this.SolutionRoot)\src\Umbraco.Web.UI.Client\bower_components")
$this.TempStoreFile("$($this.SolutionRoot)\src\Umbraco.Web.UI\web.config")
Write-Host "Create clean web.config"
$this.CopyFile("$($this.SolutionRoot)\src\Umbraco.Web.UI\web.Template.config", "$($this.SolutionRoot)\src\Umbraco.Web.UI\web.config")
Write-host "Set environment"
$env:UMBRACO_VERSION=$this.Version.Semver.ToString()
$env:UMBRACO_RELEASE=$this.Version.Release
$env:UMBRACO_COMMENT=$this.Version.Comment
$env:UMBRACO_BUILD=$this.Version.Build
if ($args -and $args[0] -eq "vso")
{
Write-host "Set VSO environment"
# 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;]$($this.Version.Semver.ToString())")
Write-Host ("##vso[task.setvariable variable=UMBRACO_RELEASE;]$($this.Version.Release)")
Write-Host ("##vso[task.setvariable variable=UMBRACO_COMMENT;]$($this.Version.Comment)")
Write-Host ("##vso[task.setvariable variable=UMBRACO_BUILD;]$($this.Version.Build)")
Write-Host ("##vso[task.setvariable variable=UMBRACO_TMP;]$($this.SolutionRoot)\build.tmp")
}
})
$ubuild.DefineMethod("PrepareNuGet",
{
Write-Host "Prepare NuGet"
# add Web.config transform files to the NuGet package
Write-Host "Add web.config transforms to NuGet package"
mv "$($this.BuildTemp)\WebApp\Views\Web.config" "$($this.BuildTemp)\WebApp\Views\Web.config.transform"
})
$ubuild.DefineMethod("RestoreNuGet",
{
Write-Host "Restore NuGet"
Write-Host "Logging to $($this.BuildTemp)\nuget.restore.log"
&$this.BuildEnv.NuGet restore "$($this.SolutionRoot)\src\Umbraco.sln" > "$($this.BuildTemp)\nuget.restore.log"
if (-not $?) { throw "Failed to restore NuGet packages." }
})
$ubuild.DefineMethod("PackageNuGet",
{
$nuspecs = "$($this.SolutionRoot)\build\NuSpecs"
Write-Host "Create NuGet packages"
&$this.BuildEnv.NuGet Pack "$nuspecs\UmbracoCms.Core.nuspec" `
-Properties BuildTmp="$($this.BuildTemp)" `
-Version "$($this.Version.Semver.ToString())" `
-Symbols -Verbosity detailed -outputDirectory "$($this.BuildOutput)" > "$($this.BuildTemp)\nupack.cmscore.log"
if (-not $?) { throw "Failed to pack NuGet UmbracoCms.Core." }
&$this.BuildEnv.NuGet Pack "$nuspecs\UmbracoCms.Web.nuspec" `
-Properties BuildTmp="$($this.BuildTemp)" `
-Version "$($this.Version.Semver.ToString())" `
-Symbols -Verbosity detailed -outputDirectory "$($this.BuildOutput)" > "$($this.BuildTemp)\nupack.cmsweb.log"
if (-not $?) { throw "Failed to pack NuGet UmbracoCms.Web." }
&$this.BuildEnv.NuGet Pack "$nuspecs\UmbracoCms.nuspec" `
-Properties BuildTmp="$($this.BuildTemp)" `
-Version "$($this.Version.Semver.ToString())" `
-Verbosity detailed -outputDirectory "$($this.BuildOutput)" > "$($this.BuildTemp)\nupack.cms.log"
if (-not $?) { throw "Failed to pack NuGet UmbracoCms." }
# run hook
if ($this.HasMethod("PostPackageNuGet"))
{
Write-Host "Run PostPackageNuGet hook"
$this.PostPackageNuGet();
if (-not $?) { throw "Failed to run hook." }
}
})
$ubuild.DefineMethod("VerifyNuGet",
{
$this.VerifyNuGetConsistency(
("UmbracoCms", "UmbracoCms.Core", "UmbracoCms.Web"),
("Umbraco.Core", "Umbraco.Web", "Umbraco.Web.UI", "Umbraco.Examine"))
if ($this.OnError()) { return }
})
$ubuild.DefineMethod("PrepareAzureGallery",
{
Write-Host "Prepare Azure Gallery"
$this.CopyFile("$($this.SolutionRoot)\build\Azure\azuregalleryrelease.ps1", $this.BuildOutput)
})
$ubuild.DefineMethod("Build",
{
$error.Clear()
$this.PrepareBuild()
if ($this.OnError()) { return }
$this.RestoreNuGet()
if ($this.OnError()) { return }
$this.CompileBelle()
if ($this.OnError()) { return }
$this.CompileUmbraco()
if ($this.OnError()) { return }
$this.PrepareTests()
if ($this.OnError()) { return }
$this.CompileTests()
if ($this.OnError()) { return }
# not running tests
$this.PreparePackages()
if ($this.OnError()) { return }
$this.PackageZip()
if ($this.OnError()) { return }
$this.VerifyNuGet()
if ($this.OnError()) { return }
$this.PrepareNuGet()
if ($this.OnError()) { return }
$this.PackageNuGet()
if ($this.OnError()) { return }
$this.PrepareAzureGallery()
if ($this.OnError()) { return }
Write-Host "Done"
})
# ################################################################
# RUN
# ################################################################
# configure
$ubuild.ReleaseBranches = @( "master" )
# run
if (-not $get)
{
$ubuild.Build()
if ($ubuild.OnError()) { return }
Write-Host "(module only: ignoring version parameter)"
}
if ($get) { return $ubuild }
else
{
Write-Host "(module only)"
}
break
}
# get build environment
Write-Host "Setup Umbraco build Environment"
$uenv = Get-UmbracoBuildEnv
# set the version if any
if (-not [string]::IsNullOrWhiteSpace($version))
{
Write-Host "Set Umbraco version to $version"
Set-UmbracoVersion $version
}
# full umbraco build
Write-Host "Build Umbraco"
Build-Umbraco

18
build/setversion.ps1 Normal file
View File

@@ -0,0 +1,18 @@
# Usage: powershell .\setversion.ps1 7.6.8
# Or: powershell .\setversion 7.6.8-beta001
param (
[Parameter(Mandatory=$true)]
[string]
$version
)
# report
Write-Host "Setting Umbraco version to $version"
# import Umbraco Build PowerShell module - $pwd is ./build
$env:PSModulePath = "$pwd\Modules\;$env:PSModulePath"
Import-Module Umbraco.Build -Force -DisableNameChecking
# run commands
$version = Set-UmbracoVersion -Version $version

7
src/NuGet.Config Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nuget.org" value="https://www.nuget.org/api/v2/" />
<add key="umbracocore" value="https://www.myget.org/F/umbracocore/api/v3/index.json" />
</packageSources>
</configuration>

View File

@@ -0,0 +1,21 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("SqlCE4Umbraco")]
[assembly: AssemblyDescription("Umbraco specific Sql Ce Provider")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyProduct("Umbraco CMS")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("04436b0a-1dc6-4ee1-9d96-4c04f1a9f429")]
[assembly: InternalsVisibleTo("Umbraco.Tests")]

View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{5BA5425F-27A7-4677-865E-82246498AA2E}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SQLCE4Umbraco</RootNamespace>
<AssemblyName>SQLCE4Umbraco</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SccProjectName>
</SccProjectName>
<SccLocalPath>
</SccLocalPath>
<SccAuxPath>
</SccAuxPath>
<SccProvider>
</SccProvider>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\SQLCE4Umbraco.XML</DocumentationFile>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data.SqlServerCe, Version=4.0.0.1, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.dll</HintPath>
</Reference>
<Reference Include="System.Data.SqlServerCe.Entity, Version=4.0.0.1, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.Entity.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\SolutionInfo.cs">
<Link>Properties\SolutionInfo.cs</Link>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SqlCeApplicationBlock.cs" />
<Compile Include="SqlCeContextGuardian.cs" />
<Compile Include="SqlCEDataReader.cs" />
<Compile Include="SqlCEHelper.cs" />
<Compile Include="SqlCEInstaller.cs" />
<Compile Include="SqlCEParameter.cs" />
<Compile Include="SqlCeProviderException.cs" />
<Compile Include="SqlCETableUtility.cs" />
<Compile Include="SqlCEUtility.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config">
<SubType>Designer</SubType>
</None>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\umbraco.datalayer\umbraco.datalayer.csproj">
<Project>{C7CB79F0-1C97-4B33-BFA7-00731B579AE2}</Project>
<Name>umbraco.datalayer</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
<!-- 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">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp50</s:String></wpf:ResourceDictionary>

View File

@@ -0,0 +1,44 @@
/************************************************************************************
*
* Umbraco Data Layer
* MIT Licensed work
* ©2008 Ruben Verborgh
*
***********************************************************************************/
using System.Data.SqlServerCe;
using umbraco.DataLayer;
namespace SqlCE4Umbraco
{
/// <summary>
/// Class that adapts a SqlDataReader.SqlDataReader to a RecordsReaderAdapter.
/// </summary>
public class SqlCeDataReaderHelper : RecordsReaderAdapter<SqlCeDataReader>
{
#region Public Constructors
/// <summary>
/// Initializes a new instance of the <see cref="SqlServerDataReader"/> class.
/// </summary>
/// <param name="dataReader">The data reader.</param>
public SqlCeDataReaderHelper(System.Data.SqlServerCe.SqlCeDataReader dataReader) : base(dataReader) { }
#endregion
#region RecordsReaderAdapter Members
/// <summary>
/// Gets a value indicating whether this instance has records.
/// </summary>
/// <value>
/// <c>true</c> if this instance has records; otherwise, <c>false</c>.
/// </value>
public override bool HasRecords
{
get { return DataReader.HasRows; }
}
#endregion
}
}

View File

@@ -0,0 +1,253 @@
/************************************************************************************
*
* Umbraco Data Layer
* MIT Licensed work
* ©2008 Ruben Verborgh
*
***********************************************************************************/
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlServerCe;
using System.Linq;
using System.Xml;
using System.Diagnostics;
using umbraco.DataLayer;
using umbraco.DataLayer.SqlHelpers.SqlServer;
namespace SqlCE4Umbraco
{
/// <summary>
/// Sql Helper for an SQL Server database.
/// </summary>
public class SqlCEHelper : SqlHelper<SqlCeParameter>
{
/// <summary>
/// Initializes a new instance of the <see cref="SqlCEHelper"/> class.
/// </summary>
/// <param name="connectionString">The connection string.</param>
public SqlCEHelper(string connectionString) : base(connectionString)
{
m_Utility = new SqlCEUtility(this);
}
/// <summary>
/// Checks if the actual database exists, if it doesn't then it will create it
/// </summary>
internal void CreateEmptyDatabase()
{
var localConnection = new SqlCeConnection(ConnectionString);
if (!System.IO.File.Exists(ReplaceDataDirectory(localConnection.Database)))
{
using (var sqlCeEngine = new SqlCeEngine(ConnectionString))
{
sqlCeEngine.CreateDatabase();
}
}
}
/// <summary>
/// Most likely only will be used for unit tests but will remove all tables from the database
/// </summary>
internal void ClearDatabase()
{
// drop constraints before tables to avoid exceptions
// looping on try/catching exceptions was not really nice
// http://stackoverflow.com/questions/536350/drop-all-the-tables-stored-procedures-triggers-constriants-and-all-the-depend
var localConnection = new SqlCeConnection(ConnectionString);
if (System.IO.File.Exists(ReplaceDataDirectory(localConnection.Database)))
{
List<string> tables;
// drop foreign keys
// SQL may need "where constraint_catalog=DB_NAME() and ..."
tables = new List<string>();
using (var reader = ExecuteReader("select table_name from information_schema.table_constraints where constraint_type = 'FOREIGN KEY' order by table_name"))
{
while (reader.Read()) tables.Add(reader.GetString("table_name").Trim());
}
foreach (var table in tables)
{
var constraints = new List<string>();
using (var reader = ExecuteReader("select constraint_name from information_schema.table_constraints where constraint_type = 'FOREIGN KEY' and table_name = '" + table + "' order by constraint_name"))
{
while (reader.Read()) constraints.Add(reader.GetString("constraint_name").Trim());
}
foreach (var constraint in constraints)
{
// SQL may need "[dbo].[table]"
ExecuteNonQuery("alter table [" + table + "] drop constraint [" + constraint + "]");
}
}
// drop primary keys
// SQL may need "where constraint_catalog=DB_NAME() and ..."
tables = new List<string>();
using (var reader = ExecuteReader("select table_name from information_schema.table_constraints where constraint_type = 'PRIMARY KEY' order by table_name"))
{
while (reader.Read()) tables.Add(reader.GetString("table_name").Trim());
}
foreach (var table in tables)
{
var constraints = new List<string>();
using (var reader = ExecuteReader("select constraint_name from information_schema.table_constraints where constraint_type = 'PRIMARY KEY' and table_name = '" + table + "' order by constraint_name"))
{
while (reader.Read()) constraints.Add(reader.GetString("constraint_name").Trim());
}
foreach (var constraint in constraints)
{
// SQL may need "[dbo].[table]"
ExecuteNonQuery("alter table [" + table + "] drop constraint [" + constraint + "]");
}
}
// drop tables
tables = new List<string>();
using (var reader = ExecuteReader("select table_name from information_schema.tables where table_type <> 'VIEW' order by table_name"))
{
while (reader.Read()) tables.Add(reader.GetString("table_name").Trim());
}
foreach (var table in tables)
{
ExecuteNonQuery("drop table [" + table + "]");
}
}
}
/// <summary>
/// Drops all foreign keys on a table.
/// </summary>
/// <param name="table">The name of the table.</param>
/// <remarks>To be used in unit tests.</remarks>
internal void DropForeignKeys(string table)
{
var constraints = new List<string>();
using (var reader = ExecuteReader("select constraint_name from information_schema.table_constraints where constraint_type = 'FOREIGN KEY' and table_name = '" + table + "' order by constraint_name"))
{
while (reader.Read()) constraints.Add(reader.GetString("constraint_name").Trim());
}
foreach (var constraint in constraints)
{
// SQL may need "[dbo].[table]"
ExecuteNonQuery("alter table [" + table + "] drop constraint [" + constraint + "]");
}
}
/// <summary>
/// Replaces the data directory with a local path.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>A local path with the resolved 'DataDirectory' mapping.</returns>
private string ReplaceDataDirectory(string path)
{
if (!string.IsNullOrWhiteSpace(path) && path.Contains("|DataDirectory|"))
{
var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory") as string;
if (!string.IsNullOrEmpty(dataDirectory))
{
path = path.Contains(@"|\")
? path.Replace("|DataDirectory|", dataDirectory)
: path.Replace("|DataDirectory|", dataDirectory + System.IO.Path.DirectorySeparatorChar);
}
}
return path;
}
/// <summary>
/// Creates a new parameter for use with this specific implementation of ISqlHelper.
/// </summary>
/// <param name="parameterName">Name of the parameter.</param>
/// <param name="value">Value of the parameter.</param>
/// <returns>A new parameter of the correct type.</returns>
/// <remarks>Abstract factory pattern</remarks>
public override IParameter CreateParameter(string parameterName, object value)
{
return new SqlCEParameter(parameterName, value);
}
/// <summary>
/// Executes a command that returns a single value.
/// </summary>
/// <param name="commandText">The command text.</param>
/// <param name="parameters">The parameters.</param>
/// <returns>The return value of the command.</returns>
protected override object ExecuteScalar(string commandText, SqlCeParameter[] parameters)
{
#if DEBUG && DebugDataLayer
// Log Query Execution
Trace.TraceInformation(GetType().Name + " SQL ExecuteScalar: " + commandText);
#endif
using (var cc = UseCurrentConnection)
{
return SqlCeApplicationBlock.ExecuteScalar(
(SqlCeConnection) cc.Connection, (SqlCeTransaction) cc.Transaction,
CommandType.Text, commandText, parameters);
}
}
/// <summary>
/// Executes a command and returns the number of rows affected.
/// </summary>
/// <param name="commandText">The command text.</param>
/// <param name="parameters">The parameters.</param>
/// <returns>
/// The number of rows affected by the command.
/// </returns>
protected override int ExecuteNonQuery(string commandText, SqlCeParameter[] parameters)
{
#if DEBUG && DebugDataLayer
// Log Query Execution
Trace.TraceInformation(GetType().Name + " SQL ExecuteNonQuery: " + commandText);
#endif
using (var cc = UseCurrentConnection)
{
return SqlCeApplicationBlock.ExecuteNonQuery(
(SqlCeConnection) cc.Connection, (SqlCeTransaction) cc.Transaction,
CommandType.Text, commandText, parameters);
}
}
/// <summary>
/// Executes a command and returns a records reader containing the results.
/// </summary>
/// <param name="commandText">The command text.</param>
/// <param name="parameters">The parameters.</param>
/// <returns>
/// A data reader containing the results of the command.
/// </returns>
protected override IRecordsReader ExecuteReader(string commandText, SqlCeParameter[] parameters)
{
#if DEBUG && DebugDataLayer
// Log Query Execution
Trace.TraceInformation(GetType().Name + " SQL ExecuteReader: " + commandText);
#endif
using (var cc = UseCurrentConnection)
{
return new SqlCeDataReaderHelper(SqlCeApplicationBlock.ExecuteReader(
(SqlCeConnection) cc.Connection, (SqlCeTransaction) cc.Transaction,
CommandType.Text, commandText, parameters));
}
}
internal IRecordsReader ExecuteReader(string commandText)
{
return ExecuteReader(commandText, new SqlCEParameter(string.Empty, string.Empty));
}
internal int ExecuteNonQuery(string commandText)
{
return ExecuteNonQuery(commandText, new SqlCEParameter(string.Empty, string.Empty));
}
}
}

View File

@@ -0,0 +1,141 @@
/************************************************************************************
*
* Umbraco Data Layer
* MIT Licensed work
* ©2008 Ruben Verborgh
*
***********************************************************************************/
using System;
using System.Resources;
using SQLCE4Umbraco;
using umbraco.DataLayer.Utility.Installer;
using System.Diagnostics;
namespace SqlCE4Umbraco
{
/// <summary>
/// Database installer for an SQL Server data source.
/// </summary>
[Obsolete("The legacy installers are no longer used and will be removed from the codebase in the future")]
public class SqlCEInstaller : DefaultInstallerUtility<SqlCEHelper>
{
#region Private Constants
/// <summary>The latest database version this installer supports.</summary>
private const DatabaseVersion LatestVersionSupported = DatabaseVersion.Version4_8;
/// <summary>The specifications to determine the database version.</summary>
private static readonly VersionSpecs[] m_VersionSpecs = new VersionSpecs[] {
new VersionSpecs("SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS LEFT OUTER JOIN umbracoApp ON appAlias = appAlias WHERE CONSTRAINT_NAME = 'FK_umbracoUser2app_umbracoApp'", 0, DatabaseVersion.Version4_8),
new VersionSpecs("SELECT id FROM umbracoNode WHERE id = -21", 1, DatabaseVersion.Version4_1),
new VersionSpecs("SELECT action FROM umbracoAppTree",DatabaseVersion.Version4),
new VersionSpecs("SELECT description FROM cmsContentType",DatabaseVersion.Version3),
new VersionSpecs("SELECT id FROM sysobjects",DatabaseVersion.None) };
#endregion
#region Public Properties
/// <summary>
/// This ensures that the database exists, then runs the base method
/// </summary>
public override bool CanConnect
{
get
{
using (var sqlHelper = SqlHelper)
sqlHelper.CreateEmptyDatabase();
return base.CanConnect;
}
}
/// <summary>
/// Gets a value indicating whether the installer can upgrade the data source.
/// </summary>
/// <value>
/// <c>true</c> if the installer can upgrade the data source; otherwise, <c>false</c>.
/// </value>
/// <remarks>Empty data sources can't be upgraded, just installed.</remarks>
public override bool CanUpgrade
{
get
{
return CurrentVersion == DatabaseVersion.Version4_1;
}
}
#endregion
#region Protected Properties
/// <summary>
/// Gets the version specification for evaluation by DetermineCurrentVersion.
/// Only first matching specification is taken into account.
/// </summary>
/// <value>The version specifications.</value>
protected override VersionSpecs[] VersionSpecs
{
get { return m_VersionSpecs; }
}
#endregion
#region Public Constructors
/// <summary>
/// Initializes a new instance of the <see cref="SqlCEInstaller"/> class.
/// </summary>
/// <param name="sqlHelper">The SQL helper.</param>
public SqlCEInstaller(SqlCEHelper sqlHelper) : base(sqlHelper, LatestVersionSupported)
{ }
#endregion
#region DefaultInstaller Members
/// <summary>
/// Returns the sql to do a full install
/// </summary>
protected override string FullInstallSql
{
get { return string.Empty; }
}
/// <summary>
/// Returns the sql to do an upgrade
/// </summary>
protected override string UpgradeSql
{
get { return string.Empty; }
}
// We need to override this as the default way of detection a db connection checks for systables that doesn't exist
// in a CE db
protected override DatabaseVersion DetermineCurrentVersion()
{
DatabaseVersion version = base.DetermineCurrentVersion();
if (version != DatabaseVersion.Unavailable)
{
return version;
}
// verify connection
try
{
using (var sqlHelper = SqlHelper)
if (SqlCeApplicationBlock.VerifyConnection(sqlHelper.ConnectionString))
return DatabaseVersion.None;
}
catch (Exception e)
{
Trace.WriteLine(e.ToString());
}
return DatabaseVersion.Unavailable;
}
#endregion
}
}

View File

@@ -0,0 +1,34 @@
/************************************************************************************
*
* Umbraco Data Layer
* MIT Licensed work
* ©2008 Ruben Verborgh
*
***********************************************************************************/
using System;
using System.Data.SqlServerCe;
using System.Data.SqlTypes;
using umbraco.DataLayer;
namespace SqlCE4Umbraco
{
/// <summary>
/// Parameter class for the SqlCEHelper.
/// </summary>
public class SqlCEParameter : SqlParameterAdapter<SqlCeParameter>
{
#region Public Constructors
/// <summary>
/// Initializes a new instance of the <see cref="SqlCEParameter"/> class.
/// </summary>
/// <param name="parameterName">Name of the parameter.</param>
/// <param name="value">Value of the parameter.</param>
public SqlCEParameter(string parameterName, object value)
: base(new SqlCeParameter(parameterName, value))
{ }
#endregion
}
}

View File

@@ -0,0 +1,250 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using umbraco.DataLayer.Utility.Table;
using umbraco.DataLayer;
using umbraco;
namespace SqlCE4Umbraco
{
/// <summary>
/// SQL Server implementation of <see cref="DefaultTableUtility&lt;S&gt;"/>.
/// </summary>
[Obsolete("The legacy installers are no longer used and will be removed from the codebase in the future")]
public class SqlCETableUtility : DefaultTableUtility<SqlCEHelper>
{
/// <summary>
/// Initializes a new instance of the <see cref="SqlCETableUtility"/> class.
/// </summary>
/// <param name="sqlHelper">The SQL helper.</param>
public SqlCETableUtility(SqlCEHelper sqlHelper)
: base(sqlHelper)
{ }
#region DefaultTableUtility<SqlCEHelper> members
/// <summary>
/// Gets the table with the specified name.
/// </summary>
/// <param name="name">The name.</param>
/// <returns>The table, or <c>null</c> if no table with that name exists.</returns>
public override ITable GetTable(string name)
{
if (name == null)
throw new ArgumentNullException("name");
ITable table = null;
// get name in correct casing
using (var sqlHelper = SqlHelper)
name = sqlHelper.ExecuteScalar<string>("SELECT name FROM sys.tables WHERE name=@name",
sqlHelper.CreateParameter("name", name));
if (name != null)
{
table = new DefaultTable(name);
using (var sqlHelper = SqlHelper)
using (IRecordsReader reader = sqlHelper.ExecuteReader(
@"SELECT c.name AS Name, st.name AS DataType, c.max_length, c.is_nullable, c.is_identity
FROM sys.tables AS t
JOIN sys.columns AS c ON t.object_id = c.object_id
JOIN sys.schemas AS s ON s.schema_id = t.schema_id
JOIN sys.types AS ty ON ty.user_type_id = c.user_type_id
JOIN sys.types st ON ty.system_type_id = st.user_type_id
WHERE t.name = @name
ORDER BY c.column_id", sqlHelper.CreateParameter("name", name)))
{
while (reader.Read())
{
table.AddField(table.CreateField(reader.GetString("Name"), GetType(reader)));
}
}
}
return table;
}
/// <summary>
/// Saves or updates the table.
/// </summary>
/// <param name="table">The table to be saved.</param>
public override void SaveTable(ITable table)
{
if (table == null)
throw new ArgumentNullException("table");
ITable oldTable = GetTable(table.Name);
// create the table if it didn't exist, update fields otherwise
if (oldTable == null)
{
CreateTable(table);
}
else
{
foreach (IField field in table)
{
// create the field if it did't exist in the old table
if (oldTable.FindField(field.Name)==null)
{
CreateColumn(table, field);
}
}
}
}
#endregion
#region Protected Helper Methods
/// <summary>
/// Creates the table in the data source.
/// </summary>
/// <param name="table">The table.</param>
protected virtual void CreateTable(ITable table)
{
Debug.Assert(table != null);
// create enumerator and check for first field
IEnumerator<IField> fieldEnumerator = table.GetEnumerator();
bool hasNext = fieldEnumerator.MoveNext();
if(!hasNext)
throw new ArgumentException("The table must contain at least one field.");
// create query
StringBuilder createTableQuery = new StringBuilder();
using (var sqlHelper = SqlHelper)
createTableQuery.AppendFormat("CREATE TABLE [{0}] (", sqlHelper.EscapeString(table.Name));
// add fields
while (hasNext)
{
// add field name and type
IField field = fieldEnumerator.Current;
createTableQuery.Append('[').Append(field.Name).Append(']');
createTableQuery.Append(' ').Append(GetDatabaseType(field));
// append comma if a following field exists
hasNext = fieldEnumerator.MoveNext();
if (hasNext)
{
createTableQuery.Append(',');
}
}
// close CREATE TABLE x (...) bracket
createTableQuery.Append(')');
// execute query
try
{
using (var sqlHelper = SqlHelper)
sqlHelper.ExecuteNonQuery(createTableQuery.ToString());
}
catch (Exception executeException)
{
throw new UmbracoException(String.Format("Could not create table '{0}'.", table), executeException);
}
}
/// <summary>
/// Creates a new column in the table.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="field">The field used to create the column.</param>
protected virtual void CreateColumn(ITable table, IField field)
{
Debug.Assert(table != null && field != null);
StringBuilder addColumnQuery = new StringBuilder();
using (var sqlHelper = SqlHelper)
addColumnQuery.AppendFormat("ALTER TABLE [{0}] ADD [{1}] {2}",
sqlHelper.EscapeString(table.Name),
sqlHelper.EscapeString(field.Name),
sqlHelper.EscapeString(GetDatabaseType(field)));
try
{
using (var sqlHelper = SqlHelper)
sqlHelper.ExecuteNonQuery(addColumnQuery.ToString());
}
catch (Exception executeException)
{
throw new UmbracoException(String.Format("Could not create column '{0}' in table '{1}'.",
field, table.Name), executeException);
}
}
/// <summary>
/// Gets the .Net type corresponding to the specified database data type.
/// </summary>
/// <param name="dataTypeReader">The data type reader.</param>
/// <returns>The .Net type</returns>
protected virtual Type GetType(IRecordsReader dataTypeReader)
{
string typeName = dataTypeReader.GetString("DataType");
switch (typeName)
{
case "bit": return typeof(bool);
case "tinyint": return typeof(byte);
case "datetime": return typeof(DateTime);
// TODO: return different decimal type according to field precision
case "decimal": return typeof(decimal);
case "uniqueidentifier": return typeof(Guid);
case "smallint": return typeof(Int16);
case "int": return typeof(Int32);
case "bigint": return typeof(Int64);
case "nvarchar": return typeof(string);
default:
throw new ArgumentException(String.Format("Cannot convert database type '{0}' to a .Net type.",
typeName));
}
}
/// <summary>
/// Gets the database type corresponding to the field, complete with field properties.
/// </summary>
/// <param name="field">The field.</param>
/// <returns>The database type.</returns>
protected virtual string GetDatabaseType(IField field)
{
StringBuilder fieldBuilder = new StringBuilder();
fieldBuilder.Append(GetDatabaseTypeName(field));
fieldBuilder.Append(field.HasProperty(FieldProperties.Identity) ? " IDENTITY(1,1)" : String.Empty);
fieldBuilder.Append(field.HasProperty(FieldProperties.NotNull) ? " NOT NULL" : " NULL");
return fieldBuilder.ToString();
}
/// <summary>
/// Gets the name of the database type, without field properties.
/// </summary>
/// <param name="field">The field.</param>
/// <returns></returns>
protected virtual string GetDatabaseTypeName(IField field)
{
switch (field.DataType.FullName)
{
case "System.Boolean": return "bit";
case "System.Byte": return "tinyint";
case "System.DateTime": return "datetime";
case "System.Decimal": return "decimal(28)";
case "System.Double": return "decimal(15)";
case "System.Single": return "decimal(7)";
case "System.Guid": return "uniqueidentifier";
case "System.Int16": return "smallint";
case "System.Int32": return "int";
case "System.Int64": return "bigint";;
case "System.String":
string type = "nvarchar";
return field.Size == 0 ? type : String.Format("{0}({1})", type, field.Size);
default:
throw new ArgumentException(String.Format("Cannot convert '{0}' to a database type.", field));
}
}
#endregion
}
}

View File

@@ -0,0 +1,56 @@
/************************************************************************************
*
* Umbraco Data Layer
* MIT Licensed work
* ©2008 Ruben Verborgh
*
***********************************************************************************/
using System;
using umbraco.DataLayer.SqlHelpers.SqlServer;
using umbraco.DataLayer.Utility;
using umbraco.DataLayer.Utility.Installer;
using umbraco.DataLayer.Utility.Table;
namespace SqlCE4Umbraco
{
/// <summary>
/// Utility for an SQL Server data source.
/// </summary>
[Obsolete("The legacy installers are no longer used and will be removed from the codebase in the future")]
public class SqlCEUtility : DefaultUtility<SqlCEHelper>
{
#region Public Constructors
/// <summary>
/// Initializes a new instance of the <see cref="SqlServerUtility"/> class.
/// </summary>
/// <param name="sqlHelper">The SQL helper.</param>
public SqlCEUtility(SqlCEHelper sqlHelper) : base(sqlHelper)
{ }
#endregion
#region DefaultUtility Members
/// <summary>
/// Creates an installer.
/// </summary>
/// <returns>The SQL Server installer.</returns>
public override IInstallerUtility CreateInstaller()
{
return new SqlCEInstaller(SqlHelper);
}
/// <summary>
/// Creates a table utility.
/// </summary>
/// <returns>The table utility</returns>
public override ITableUtility CreateTableUtility()
{
return new SqlCETableUtility(SqlHelper);
}
#endregion
}
}

View File

@@ -0,0 +1,270 @@
using System;
using System.Collections.Generic;
using System.Data.SqlServerCe;
using System.Data;
using System.Diagnostics;
using SQLCE4Umbraco;
namespace SqlCE4Umbraco
{
public class SqlCeApplicationBlock
{
/// <summary>
///
/// </summary>
/// <param name="connectionString"></param>
/// <param name="commandType"></param>
/// <param name="commandText"></param>
/// <param name="commandParameters"></param>
/// <returns></returns>
public static object ExecuteScalar(
string connectionString,
CommandType commandType,
string commandText,
params SqlCeParameter[] commandParameters
)
{
try
{
using (var conn = SqlCeContextGuardian.Open(connectionString))
{
return ExecuteScalarTry(conn, null, commandText, commandParameters);
}
}
catch (Exception ee)
{
throw new SqlCeProviderException("Error running Scalar: \nSQL Statement:\n" + commandText + "\n\nException:\n" + ee);
}
}
public static object ExecuteScalar(
SqlCeConnection conn, SqlCeTransaction trx,
CommandType commandType,
string commandText,
params SqlCeParameter[] commandParameters)
{
try
{
return ExecuteScalarTry(conn, trx, commandText, commandParameters);
}
catch (Exception ee)
{
throw new SqlCeProviderException("Error running Scalar: \nSQL Statement:\n" + commandText + "\n\nException:\n" + ee);
}
}
public static object ExecuteScalar(
SqlCeConnection conn,
CommandType commandType,
string commandText,
params SqlCeParameter[] commandParameters)
{
return ExecuteScalar(conn, null, commandType, commandText, commandParameters);
}
private static object ExecuteScalarTry(
SqlCeConnection conn, SqlCeTransaction trx,
string commandText,
params SqlCeParameter[] commandParameters)
{
object retVal;
using (var cmd = trx == null ? new SqlCeCommand(commandText, conn) : new SqlCeCommand(commandText, conn, trx))
{
AttachParameters(cmd, commandParameters);
Debug.WriteLine("---------------------------------SCALAR-------------------------------------");
Debug.WriteLine(commandText);
Debug.WriteLine("----------------------------------------------------------------------------");
retVal = cmd.ExecuteScalar();
}
return retVal;
}
/// <summary>
///
/// </summary>
/// <param name="connectionString"></param>
/// <param name="commandType"></param>
/// <param name="commandText"></param>
/// <param name="commandParameters"></param>
public static int ExecuteNonQuery(
string connectionString,
CommandType commandType,
string commandText,
params SqlCeParameter[] commandParameters
)
{
try
{
using (var conn = SqlCeContextGuardian.Open(connectionString))
{
return ExecuteNonQueryTry(conn, null, commandText, commandParameters);
}
}
catch (Exception ee)
{
throw new SqlCeProviderException("Error running NonQuery: \nSQL Statement:\n" + commandText + "\n\nException:\n" + ee.ToString());
}
}
public static int ExecuteNonQuery(
SqlCeConnection conn,
CommandType commandType,
string commandText,
params SqlCeParameter[] commandParameters
)
{
return ExecuteNonQuery(conn, null, commandType, commandText, commandParameters);
}
public static int ExecuteNonQuery(
SqlCeConnection conn, SqlCeTransaction trx,
CommandType commandType,
string commandText,
params SqlCeParameter[] commandParameters
)
{
try
{
return ExecuteNonQueryTry(conn, trx, commandText, commandParameters);
}
catch (Exception ee)
{
throw new SqlCeProviderException("Error running NonQuery: \nSQL Statement:\n" + commandText + "\n\nException:\n" + ee.ToString());
}
}
private static int ExecuteNonQueryTry(
SqlCeConnection conn, SqlCeTransaction trx,
string commandText,
params SqlCeParameter[] commandParameters)
{
// this is for multiple queries in the installer
if (commandText.Trim().StartsWith("!!!"))
{
commandText = commandText.Trim().Trim('!');
var commands = commandText.Split('|');
var currentCmd = string.Empty;
foreach (var command in commands)
{
try
{
currentCmd = command;
if (string.IsNullOrWhiteSpace(command)) continue;
var c = trx == null ? new SqlCeCommand(command, conn) : new SqlCeCommand(command, conn, trx);
c.ExecuteNonQuery();
}
catch (Exception e)
{
Debug.WriteLine("*******************************************************************");
Debug.WriteLine(currentCmd);
Debug.WriteLine(e);
Debug.WriteLine("*******************************************************************");
}
}
return 1;
}
Debug.WriteLine("----------------------------------------------------------------------------");
Debug.WriteLine(commandText);
Debug.WriteLine("----------------------------------------------------------------------------");
var cmd = new SqlCeCommand(commandText, conn);
AttachParameters(cmd, commandParameters);
var rowsAffected = cmd.ExecuteNonQuery();
return rowsAffected;
}
/// <summary>
///
/// </summary>
/// <param name="connectionString"></param>
/// <param name="commandType"></param>
/// <param name="commandText"></param>
/// <param name="commandParameters"></param>
/// <returns></returns>
public static SqlCeDataReader ExecuteReader(
string connectionString,
CommandType commandType,
string commandText,
params SqlCeParameter[] commandParameters
)
{
try
{
var conn = SqlCeContextGuardian.Open(connectionString);
return ExecuteReaderTry(conn, null, commandText, commandParameters);
}
catch (Exception ee)
{
throw new SqlCeProviderException("Error running Reader: \nSQL Statement:\n" + commandText + "\n\nException:\n" + ee.ToString());
}
}
public static SqlCeDataReader ExecuteReader(
SqlCeConnection conn,
CommandType commandType,
string commandText,
params SqlCeParameter[] commandParameters
)
{
return ExecuteReader(conn, commandType, commandText, commandParameters);
}
public static SqlCeDataReader ExecuteReader(
SqlCeConnection conn, SqlCeTransaction trx,
CommandType commandType,
string commandText,
params SqlCeParameter[] commandParameters
)
{
try
{
return ExecuteReaderTry(conn, trx, commandText, commandParameters);
}
catch (Exception ee)
{
throw new SqlCeProviderException("Error running Reader: \nSQL Statement:\n" + commandText + "\n\nException:\n" + ee.ToString());
}
}
private static SqlCeDataReader ExecuteReaderTry(
SqlCeConnection conn, SqlCeTransaction trx,
string commandText,
params SqlCeParameter[] commandParameters)
{
Debug.WriteLine("---------------------------------READER-------------------------------------");
Debug.WriteLine(commandText);
Debug.WriteLine("----------------------------------------------------------------------------");
try
{
var cmd = trx == null ? new SqlCeCommand(commandText, conn) : new SqlCeCommand(commandText, conn, trx);
AttachParameters(cmd, commandParameters);
return cmd.ExecuteReader();
}
catch
{
conn.Close();
throw;
}
}
public static bool VerifyConnection(string connectionString)
{
using (var conn = SqlCeContextGuardian.Open(connectionString))
{
return conn.State == ConnectionState.Open;
}
}
private static void AttachParameters(SqlCeCommand command, IEnumerable<SqlCeParameter> commandParameters)
{
foreach (var parameter in commandParameters)
{
if ((parameter.Direction == ParameterDirection.InputOutput) && (parameter.Value == null))
parameter.Value = DBNull.Value;
command.Parameters.Add(parameter);
}
}
}
}

View File

@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlServerCe;
using System.Linq;
using System.Text;
namespace SQLCE4Umbraco
{
public static class SqlCeContextGuardian
{
private static SqlCeConnection _constantOpenConnection;
private static readonly object Lock = new object();
// Awesome SQL CE 4 speed improvement by Erik Ejskov Jensen - SQL CE 4 MVP
// It's not an issue with SQL CE 4 that we never close the connection
public static SqlCeConnection Open(string connectionString)
{
var connectionStringBuilder = new DbConnectionStringBuilder();
try
{
connectionStringBuilder.ConnectionString = connectionString;
}
catch (Exception ex)
{
throw new ArgumentException("Bad connection string.", "connectionString", ex);
}
connectionStringBuilder.Remove("datalayer");
// SQL CE 4 performs better when there's always a connection open in the background
EnsureOpenBackgroundConnection(connectionStringBuilder.ConnectionString);
SqlCeConnection conn = new SqlCeConnection(connectionStringBuilder.ConnectionString);
conn.Open();
return conn;
}
/// <summary>
/// Sometimes we need to ensure this is closed especially in unit tests
/// </summary>
internal static void CloseBackgroundConnection()
{
if (_constantOpenConnection != null)
_constantOpenConnection.Close();
}
private static void EnsureOpenBackgroundConnection(string connectionString)
{
lock (Lock)
{
if (_constantOpenConnection == null)
{
_constantOpenConnection = new SqlCeConnection(connectionString);
_constantOpenConnection.Open();
}
else if (_constantOpenConnection.State != ConnectionState.Open)
_constantOpenConnection.Open();
}
}
}
}

View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SqlCE4Umbraco
{
public class SqlCeProviderException : Exception
{
public SqlCeProviderException() : base()
{
}
public SqlCeProviderException(string message) : base(message)
{
}
}
}

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin.Security.OAuth" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin.Security.Cookies" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="log4net" publicKeyToken="669e0ddf0bb1aa2a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.0.8.0" newVersion="2.0.8.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /></startup></configuration>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="SqlServerCE" version="4.0.0.1" targetFramework="net45" />
</packages>

View File

@@ -1,22 +1,15 @@
using System.Reflection;
using System.Resources;
[assembly: AssemblyCompany("Umbraco")]
[assembly: AssemblyCopyright("Copyright © Umbraco 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en-US")]
// versions
// read https://stackoverflow.com/questions/64602/what-are-differences-between-assemblyversion-assemblyfileversion-and-assemblyin
// note: do NOT change anything here manually, use the build scripts
// this is the ONLY ONE the CLR cares about for compatibility
// should change ONLY when "hard" breaking compatibility (manual change)
[assembly: AssemblyVersion("8.0.0")]
// these are FYI and changed automatically
[assembly: AssemblyFileVersion("8.0.0")]
[assembly: AssemblyInformationalVersion("8.0.0-alpha.52")]
using System.Reflection;
using System.Resources;
[assembly: AssemblyCompany("Umbraco")]
[assembly: AssemblyCopyright("Copyright © Umbraco 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguageAttribute("en-US")]
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.13.0")]
[assembly: AssemblyInformationalVersion("7.13.0")]

View File

@@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Umbraco.Core.Logging;
using Umbraco.Core.ObjectResolution;
using umbraco.interfaces;
namespace Umbraco.Core
{
/// <summary>
/// A resolver to return all IAction objects
/// </summary>
public sealed class ActionsResolver : LazyManyObjectsResolverBase<ActionsResolver, IAction>
{
/// <summary>
/// Constructor
/// </summary>
/// <param name="serviceProvider"></param>
/// <param name="logger"></param>
/// <param name="packageActions"></param>
internal ActionsResolver(IServiceProvider serviceProvider, ILogger logger, Func<IEnumerable<Type>> packageActions)
: base(serviceProvider, logger, packageActions)
{
}
/// <summary>
/// Gets the <see cref="IAction"/> implementations.
/// </summary>
public IEnumerable<IAction> Actions
{
get
{
return Values;
}
}
/// <summary>
/// This method will return a list of IAction's based on a string (letter) list. Each character in the list may represent
/// an IAction. This will associate any found IActions based on the Letter property of the IAction with the character being referenced.
/// </summary>
/// <param name="actions"></param>
/// <returns>returns a list of actions that have an associated letter found in the action string list</returns>
public IEnumerable<IAction> FromActionSymbols(IEnumerable<string> actions)
{
var allActions = Actions.ToArray();
return actions
.Select(c => allActions.FirstOrDefault(a => a.Letter.ToString(CultureInfo.InvariantCulture) == c))
.WhereNotNull()
.ToArray();
}
/// <summary>
/// Returns the string (letter) representation of the actions that make up the actions collection
/// </summary>
/// <returns></returns>
public IEnumerable<string> ToActionSymbols(IEnumerable<IAction> actions)
{
return actions.Select(x => x.Letter.ToString(CultureInfo.InvariantCulture)).ToArray();
}
/// <summary>
/// Gets an Action if it exists.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
internal IAction GetAction<T>()
where T : IAction
{
return Actions.SingleOrDefault(x => x.GetType() == typeof (T));
}
protected override IEnumerable<IAction> CreateInstances()
{
var actions = new List<IAction>();
var foundIActions = InstanceTypes;
foreach (var type in foundIActions)
{
IAction typeInstance;
var instance = type.GetProperty("Instance", BindingFlags.Public | BindingFlags.Static);
//if the singletone initializer is not found, try simply creating an instance of the IAction if it supports public constructors
if (instance == null)
typeInstance = ServiceProvider.GetService(type) as IAction;
else
typeInstance = instance.GetValue(null, null) as IAction;
if (typeInstance != null)
{
actions.Add(typeInstance);
}
}
return actions;
}
}
}

View File

@@ -0,0 +1,20 @@
namespace Umbraco.Core
{
/// <summary>
/// Helper methods for Activation
/// </summary>
internal static class ActivatorHelper
{
/// <summary>
/// Creates an instance of a type using that type's default constructor.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T CreateInstance<T>() where T : class, new()
{
return new ActivatorServiceProvider().GetService(typeof (T)) as T;
}
}
}

View File

@@ -0,0 +1,12 @@
using System;
namespace Umbraco.Core
{
internal class ActivatorServiceProvider : IServiceProvider
{
public object GetService(Type serviceType)
{
return Activator.CreateInstance(serviceType);
}
}
}

View File

@@ -0,0 +1,506 @@
using Semver;
using System;
using System.Collections.Concurrent;
using System.Configuration;
using System.Threading;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.ObjectResolution;
using Umbraco.Core.Profiling;
using Umbraco.Core.Scoping;
using Umbraco.Core.Services;
using Umbraco.Core.Sync;
namespace Umbraco.Core
{
/// <summary>
/// the Umbraco Application context
/// </summary>
/// <remarks>
/// one per AppDomain, represents the global Umbraco application
/// </remarks>
public class ApplicationContext : IDisposable
{
/// <summary>
/// Constructor
/// </summary>
/// <param name="dbContext"></param>
/// <param name="serviceContext"></param>
/// <param name="cache"></param>
/// <param name="logger"></param>
public ApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext, CacheHelper cache, ProfilingLogger logger)
{
if (dbContext == null) throw new ArgumentNullException("dbContext");
if (serviceContext == null) throw new ArgumentNullException("serviceContext");
if (cache == null) throw new ArgumentNullException("cache");
if (logger == null) throw new ArgumentNullException("logger");
_databaseContext = dbContext;
_services = serviceContext;
ApplicationCache = cache;
ProfilingLogger = logger;
Init();
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="dbContext"></param>
/// <param name="serviceContext"></param>
/// <param name="cache"></param>
[Obsolete("Use the other constructor specifying a ProfilingLogger instead")]
public ApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext, CacheHelper cache)
: this(dbContext, serviceContext, cache,
new ProfilingLogger(LoggerResolver.Current.Logger, ProfilerResolver.Current.Profiler))
{
}
/// <summary>
/// Creates a basic app context
/// </summary>
/// <param name="cache"></param>
[Obsolete("Use the other constructor specifying a ProfilingLogger instead")]
public ApplicationContext(CacheHelper cache)
{
if (cache == null) throw new ArgumentNullException("cache");
ApplicationCache = cache;
ProfilingLogger = new ProfilingLogger(LoggerResolver.Current.Logger, ProfilerResolver.Current.Profiler);
Init();
}
/// <summary>
/// Creates a basic app context
/// </summary>
/// <param name="cache"></param>
/// <param name="logger"></param>
public ApplicationContext(CacheHelper cache, ProfilingLogger logger)
{
if (cache == null) throw new ArgumentNullException("cache");
if (logger == null) throw new ArgumentNullException("logger");
ApplicationCache = cache;
ProfilingLogger = logger;
Init();
}
/// <summary>
/// A method used to set and/or ensure that a global ApplicationContext singleton is created.
/// </summary>
/// <param name="appContext">
/// The instance to set on the global application singleton
/// </param>
/// <param name="replaceContext">If set to true and the singleton is already set, it will be replaced</param>
/// <returns></returns>
/// <remarks>
/// This is NOT thread safe
/// </remarks>
public static ApplicationContext EnsureContext(ApplicationContext appContext, bool replaceContext)
{
if (Current != null)
{
if (!replaceContext)
return Current;
}
Current = appContext;
return Current;
}
/// <summary>
/// A method used to create and ensure that a global ApplicationContext singleton is created.
/// </summary>
/// <param name="cache"></param>
/// <param name="replaceContext">
/// If set to true will replace the current singleton instance - This should only be used for unit tests or on app
/// startup if for some reason the boot manager is not the umbraco boot manager.
/// </param>
/// <param name="dbContext"></param>
/// <param name="serviceContext"></param>
/// <returns></returns>
/// <remarks>
/// This is NOT thread safe
/// </remarks>
[Obsolete("Use the other method specifying an ProfilingLogger instead")]
public static ApplicationContext EnsureContext(DatabaseContext dbContext, ServiceContext serviceContext, CacheHelper cache, bool replaceContext)
{
if (Current != null)
{
if (!replaceContext)
return Current;
}
var ctx = new ApplicationContext(dbContext, serviceContext, cache);
Current = ctx;
return Current;
}
/// <summary>
/// A method used to create and ensure that a global ApplicationContext singleton is created.
/// </summary>
/// <param name="cache"></param>
/// <param name="logger"></param>
/// <param name="replaceContext">
/// If set to true will replace the current singleton instance - This should only be used for unit tests or on app
/// startup if for some reason the boot manager is not the umbraco boot manager.
/// </param>
/// <param name="dbContext"></param>
/// <param name="serviceContext"></param>
/// <returns></returns>
/// <remarks>
/// This is NOT thread safe
/// </remarks>
public static ApplicationContext EnsureContext(DatabaseContext dbContext, ServiceContext serviceContext, CacheHelper cache, ProfilingLogger logger, bool replaceContext)
{
if (Current != null)
{
if (!replaceContext)
return Current;
}
var ctx = new ApplicationContext(dbContext, serviceContext, cache, logger);
Current = ctx;
return Current;
}
/// <summary>
/// Singleton accessor
/// </summary>
public static ApplicationContext Current { get; internal set; }
/// <summary>
/// Gets the scope provider.
/// </summary>
internal IScopeProvider ScopeProvider { get { return _databaseContext == null ? null : _databaseContext.ScopeProvider; } }
/// <summary>
/// Returns the application wide cache accessor
/// </summary>
/// <remarks>
/// Any caching that is done in the application (app wide) should be done through this property
/// </remarks>
public CacheHelper ApplicationCache { get; private set; }
/// <summary>
/// Exposes the global ProfilingLogger - this should generally not be accessed via the UmbracoContext and should normally just be exposed
/// on most base classes or injected with IoC
/// </summary>
public ProfilingLogger ProfilingLogger { get; private set; }
// IsReady is set to true by the boot manager once it has successfully booted
// note - the original umbraco module checks on content.Instance in umbraco.dll
// now, the boot task that setup the content store ensures that it is ready
bool _isReady = false;
readonly ManualResetEventSlim _isReadyEvent = new ManualResetEventSlim(false);
private DatabaseContext _databaseContext;
private ServiceContext _services;
public bool IsReady
{
get
{
return _isReady;
}
internal set
{
AssertIsNotReady();
_isReady = value;
_isReadyEvent.Set();
}
}
public bool WaitForReady(int timeout)
{
return _isReadyEvent.WaitHandle.WaitOne(timeout);
}
// notes
// GlobalSettings.ConfigurationStatus returns the value that's in the web.config, so it's the "configured version"
// GlobalSettings.CurrentVersion returns the hard-coded "current version"
// the system is configured if they match
// if they don't, install runs, updates web.config (presumably) and updates GlobalSettings.ConfiguredStatus
public bool IsConfigured
{
get { return _configured.Value; }
}
/// <summary>
/// If the db is configured, there is a database context and there is an umbraco schema, but we are not 'configured' , then it means we are upgrading
/// </summary>
public bool IsUpgrading
{
get
{
if (IsConfigured == false
&& DatabaseContext != null
&& DatabaseContext.IsDatabaseConfigured)
{
var schemaresult = DatabaseContext.ValidateDatabaseSchema();
if (schemaresult.ValidTables.Count > 0) return true;
}
return false;
}
}
/// <summary>
/// The application url.
/// </summary>
/// <remarks>
/// The application url is the url that should be used by services to talk to the application,
/// eg keep alive or scheduled publishing services. It must exist on a global context because
/// some of these services may not run within a web context.
/// The format of the application url is:
/// - has a scheme (http or https)
/// - has the SystemDirectories.Umbraco path
/// - does not end with a slash
/// It is initialized on the first request made to the server, by UmbracoModule.EnsureApplicationUrl:
/// - if umbracoSettings:settings/web.routing/@umbracoApplicationUrl is set, use the value (new setting)
/// - if umbracoSettings:settings/scheduledTasks/@baseUrl is set, use the value (backward compatibility)
/// - otherwise, use the url of the (first) request.
/// Not locking, does not matter if several threads write to this.
/// See also issues:
/// - http://issues.umbraco.org/issue/U4-2059
/// - http://issues.umbraco.org/issue/U4-6788
/// - http://issues.umbraco.org/issue/U4-5728
/// - http://issues.umbraco.org/issue/U4-5391
/// </remarks>
public string UmbracoApplicationUrl
{
get
{
ApplicationUrlHelper.EnsureApplicationUrl(this);
return _umbracoApplicationUrl;
}
}
/// <summary>
/// Resets the url.
/// </summary>
public void ResetUmbracoApplicationUrl()
{
_umbracoApplicationUrl = null;
}
// ReSharper disable once InconsistentNaming
internal string _umbracoApplicationUrl;
internal ConcurrentDictionary<string, string> _umbracoApplicationDomains = new ConcurrentDictionary<string, string>();
internal string _umbracoApplicationDeploymentId;
private Lazy<bool> _configured;
internal MainDom MainDom { get; private set; }
private void Init()
{
MainDom = new MainDom(ProfilingLogger.Logger);
MainDom.Acquire();
//Create the lazy value to resolve whether or not the application is 'configured'
_configured = new Lazy<bool>(() =>
{
try
{
var configStatus = ConfigurationStatus;
var currentVersion = UmbracoVersion.GetSemanticVersion();
var ok =
//we are not configured if this is null
string.IsNullOrWhiteSpace(configStatus) == false
//they must match
&& configStatus == currentVersion;
if (ok)
{
//The versions are the same in config, but are they the same in the database. We can only check this
// if we have a db context available, if we don't then we are not installed anyways
if (DatabaseContext.IsDatabaseConfigured && DatabaseContext.CanConnect)
{
var found = Services.MigrationEntryService.FindEntry(Constants.System.UmbracoMigrationName, UmbracoVersion.GetSemanticVersion());
if (found == null)
{
//we haven't executed this migration in this environment, so even though the config versions match,
// this db has not been updated.
ProfilingLogger.Logger.Debug<ApplicationContext>(string.Format("The migration for version: '{0} has not been executed, there is no record in the database", currentVersion.ToSemanticString()));
ok = false;
}
}
}
else
{
ProfilingLogger.Logger.Debug<ApplicationContext>(string.Format("CurrentVersion different from configStatus: '{0}','{1}'", currentVersion.ToSemanticString(), configStatus));
}
return ok;
}
catch (Exception ex)
{
LogHelper.Error<ApplicationContext>("Error determining if application is configured, returning false", ex);
return false;
}
});
}
private string ConfigurationStatus
{
get
{
try
{
return ConfigurationManager.AppSettings["umbracoConfigurationStatus"];
}
catch
{
return String.Empty;
}
}
}
/// <summary>
/// Gets the Current Version of the Umbraco Site before an upgrade
/// by using the last/most recent Umbraco Migration that has been run
/// </summary>
/// <returns>A SemVersion of the latest Umbraco DB Migration run</returns>
/// <remarks>
/// NOTE: This existed in the InstallHelper previously but should really be here so it can be re-used if necessary
/// </remarks>
internal SemVersion CurrentVersion()
{
//Set a default version of 0.0.0
var version = new SemVersion(0);
//If we have a db context available, if we don't then we are not installed anyways
if (DatabaseContext.IsDatabaseConfigured && DatabaseContext.CanConnect)
version = DatabaseContext.ValidateDatabaseSchema().DetermineInstalledVersionByMigrations(Services.MigrationEntryService);
if (version != new SemVersion(0))
return version;
// If we aren't able to get a result from the umbracoMigrations table then use the version in web.config, if it's available
if (string.IsNullOrWhiteSpace(GlobalSettings.ConfigurationStatus))
return version;
var configuredVersion = GlobalSettings.ConfigurationStatus;
string currentComment = null;
var current = configuredVersion.Split('-');
if (current.Length > 1)
currentComment = current[1];
Version currentVersion;
if (Version.TryParse(current[0], out currentVersion))
{
version = new SemVersion(
currentVersion.Major,
currentVersion.Minor,
currentVersion.Build,
string.IsNullOrWhiteSpace(currentComment) ? null : currentComment,
currentVersion.Revision > 0 ? currentVersion.Revision.ToString() : null);
}
return version;
}
private void AssertIsNotReady()
{
if (this.IsReady)
throw new Exception("ApplicationContext has already been initialized.");
}
/// <summary>
/// Gets the current DatabaseContext
/// </summary>
/// <remarks>
/// Internal set is generally only used for unit tests
/// </remarks>
public DatabaseContext DatabaseContext
{
get
{
if (_databaseContext == null)
throw new InvalidOperationException("The DatabaseContext has not been set on the ApplicationContext");
return _databaseContext;
}
internal set { _databaseContext = value; }
}
/// <summary>
/// Gets the current ServiceContext
/// </summary>
/// <remarks>
/// Internal set is generally only used for unit tests
/// </remarks>
public ServiceContext Services
{
get
{
if (_services == null)
throw new InvalidOperationException("The ServiceContext has not been set on the ApplicationContext");
return _services;
}
internal set { _services = value; }
}
internal ServerRole GetCurrentServerRole()
{
var registrar = ServerRegistrarResolver.Current.Registrar as IServerRegistrar2;
return registrar == null ? ServerRole.Unknown : registrar.GetCurrentServerRole();
}
private volatile bool _disposed;
private readonly ReaderWriterLockSlim _disposalLocker = new ReaderWriterLockSlim();
/// <summary>
/// This will dispose and reset all resources used to run the application
/// </summary>
/// <remarks>
/// IMPORTANT: Never dispose this object if you require the Umbraco application to run, disposing this object
/// is generally used for unit testing and when your application is shutting down after you have booted Umbraco.
/// </remarks>
void IDisposable.Dispose()
{
// Only operate if we haven't already disposed
if (_disposed) return;
using (new WriteLock(_disposalLocker))
{
// Check again now we're inside the lock
if (_disposed) return;
//clear the cache
if (ApplicationCache != null)
{
ApplicationCache.RuntimeCache.ClearAllCache();
ApplicationCache.IsolatedRuntimeCache.ClearAllCaches();
}
//reset all resolvers
ResolverCollection.ResetAll();
//reset resolution itself (though this should be taken care of by resetting any of the resolvers above)
Resolution.Reset();
//reset the instance objects
this.ApplicationCache = null;
if (_databaseContext != null) //need to check the internal field here
{
if (_databaseContext.ScopeProvider.AmbientScope != null)
{
var scope = _databaseContext.ScopeProvider.AmbientScope;
scope.Dispose();
}
/*
if (DatabaseContext.IsDatabaseConfigured && DatabaseContext.Database != null)
{
DatabaseContext.Database.Dispose();
}
*/
}
this.DatabaseContext = null;
this.Services = null;
this._isReady = false; //set the internal field
// Indicate that the instance has been disposed.
_disposed = true;
}
}
}
}

View File

@@ -0,0 +1,119 @@
namespace Umbraco.Core
{
/// <summary>
/// A plugin type that allows developers to execute code during the Umbraco bootup process
/// </summary>
/// <remarks>
/// Allows you to override the methods that you would like to execute code for: ApplicationInitialized, ApplicationStarting, ApplicationStarted.
///
/// By default none of these methods will execute if the Umbraco application is not configured or if the Umbraco database is not configured, however
/// if you need these methods to execute even if either of these are not configured you can override the properties:
/// ExecuteWhenApplicationNotConfigured and ExecuteWhenDatabaseNotConfigured
/// </remarks>
public abstract class ApplicationEventHandler : IApplicationEventHandler
{
public void OnApplicationInitialized(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
if (ShouldExecute(applicationContext))
{
ApplicationInitialized(umbracoApplication, applicationContext);
}
}
public void OnApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
if (ShouldExecute(applicationContext))
{
ApplicationStarting(umbracoApplication, applicationContext);
}
}
public void OnApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
if (ShouldExecute(applicationContext))
{
ApplicationStarted(umbracoApplication, applicationContext);
}
}
/// <summary>
/// Overridable method to execute when the ApplicationContext is created and other static objects that require initialization have been setup
/// </summary>
/// <param name="umbracoApplication"></param>
/// <param name="applicationContext"></param>
protected virtual void ApplicationInitialized(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
}
/// <summary>
/// Overridable method to execute when All resolvers have been initialized but resolution is not frozen so they can be modified in this method
/// </summary>
/// <param name="umbracoApplication"></param>
/// <param name="applicationContext"></param>
protected virtual void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
}
/// <summary>
/// Overridable method to execute when Bootup is completed, this allows you to perform any other bootup logic required for the application.
/// Resolution is frozen so now they can be used to resolve instances.
/// </summary>
/// <param name="umbracoApplication"></param>
/// <param name="applicationContext"></param>
protected virtual void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
}
/// <summary>
/// Determine if the methods should execute based on the configuration status of the application.
/// </summary>
/// <param name="applicationContext"></param>
/// <returns></returns>
private bool ShouldExecute(ApplicationContext applicationContext)
{
if (applicationContext.IsConfigured && applicationContext.DatabaseContext.IsDatabaseConfigured)
{
return true;
}
if (!applicationContext.IsConfigured && ExecuteWhenApplicationNotConfigured)
{
return true;
}
if (!applicationContext.DatabaseContext.IsDatabaseConfigured && ExecuteWhenDatabaseNotConfigured)
{
return true;
}
return false;
}
/// <summary>
/// A flag to determine if the overridable methods for this class will execute even if the
/// Umbraco application is not configured
/// </summary>
/// <remarks>
/// An Umbraco Application is not configured when it requires a new install or upgrade. When the latest version in the
/// assembly does not match the version in the config.
/// </remarks>
protected virtual bool ExecuteWhenApplicationNotConfigured
{
get { return false; }
}
/// <summary>
/// A flag to determine if the overridable methods for this class will execute even if the
/// Umbraco database is not configured
/// </summary>
/// <remarks>
/// The Umbraco database is not configured when we cannot connect to the database or when the database tables are not installed.
/// </remarks>
protected virtual bool ExecuteWhenDatabaseNotConfigured
{
get { return false; }
}
}
}

View File

@@ -1,72 +1,72 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
namespace Umbraco.Core
{
internal static class AssemblyExtensions
{
/// <summary>
/// Returns the file used to load the assembly
/// </summary>
/// <param name="assembly"></param>
/// <returns></returns>
public static FileInfo GetAssemblyFile(this Assembly assembly)
{
var codeBase = assembly.CodeBase;
var uri = new Uri(codeBase);
var path = uri.LocalPath;
return new FileInfo(path);
}
/// <summary>
/// Returns true if the assembly is the App_Code assembly
/// </summary>
/// <param name="assembly"></param>
/// <returns></returns>
public static bool IsAppCodeAssembly(this Assembly assembly)
{
if (assembly.FullName.StartsWith("App_Code"))
{
try
{
Assembly.Load("App_Code");
return true;
}
catch (FileNotFoundException)
{
//this will occur if it cannot load the assembly
return false;
}
}
return false;
}
/// <summary>
/// Returns true if the assembly is the compiled global asax.
/// </summary>
/// <param name="assembly"></param>
/// <returns></returns>
public static bool IsGlobalAsaxAssembly(this Assembly assembly)
{
//only way I can figure out how to test is by the name
return assembly.FullName.StartsWith("App_global.asax");
}
/// <summary>
/// Returns the file used to load the assembly
/// </summary>
/// <param name="assemblyName"></param>
/// <returns></returns>
public static FileInfo GetAssemblyFile(this AssemblyName assemblyName)
{
var codeBase = assemblyName.CodeBase;
var uri = new Uri(codeBase);
var path = uri.LocalPath;
return new FileInfo(path);
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
namespace Umbraco.Core
{
internal static class AssemblyExtensions
{
/// <summary>
/// Returns the file used to load the assembly
/// </summary>
/// <param name="assembly"></param>
/// <returns></returns>
public static FileInfo GetAssemblyFile(this Assembly assembly)
{
var codeBase = assembly.CodeBase;
var uri = new Uri(codeBase);
var path = uri.LocalPath;
return new FileInfo(path);
}
/// <summary>
/// Returns true if the assembly is the App_Code assembly
/// </summary>
/// <param name="assembly"></param>
/// <returns></returns>
public static bool IsAppCodeAssembly(this Assembly assembly)
{
if (assembly.FullName.StartsWith("App_Code"))
{
try
{
Assembly.Load("App_Code");
return true;
}
catch (FileNotFoundException)
{
//this will occur if it cannot load the assembly
return false;
}
}
return false;
}
/// <summary>
/// Returns true if the assembly is the compiled global asax.
/// </summary>
/// <param name="assembly"></param>
/// <returns></returns>
public static bool IsGlobalAsaxAssembly(this Assembly assembly)
{
//only way I can figure out how to test is by the name
return assembly.FullName.StartsWith("App_global.asax");
}
/// <summary>
/// Returns the file used to load the assembly
/// </summary>
/// <param name="assemblyName"></param>
/// <returns></returns>
public static FileInfo GetAssemblyFile(this AssemblyName assemblyName)
{
var codeBase = assemblyName.CodeBase;
var uri = new Uri(codeBase);
var path = uri.LocalPath;
return new FileInfo(path);
}
}
}

View File

@@ -69,11 +69,11 @@ namespace Umbraco.Core
public Task<IDisposable> LockAsync()
{
var wait = _semaphore != null
var wait = _semaphore != null
? _semaphore.WaitAsync()
: _semaphore2.WaitOneAsync();
return wait.IsCompleted
return wait.IsCompleted
? _releaserTask ?? Task.FromResult(CreateReleaser()) // anonymous vs named
: wait.ContinueWith((_, state) => (((AsyncLock) state).CreateReleaser()),
this, CancellationToken.None,
@@ -112,7 +112,7 @@ namespace Umbraco.Core
return _releaser ?? CreateReleaser(); // anonymous vs named
}
// note - before making those classes some structs, read
// note - before making those classes some structs, read
// about "impure methods" and mutating readonly structs...
private class NamedSemaphoreReleaser : CriticalFinalizerObject, IDisposable
@@ -140,9 +140,9 @@ namespace Umbraco.Core
_semaphore.Dispose();
}
// we WANT to release the semaphore because it's a system object, ie a critical
// we WANT to release the semaphore because it's a system object, ie a critical
// non-managed resource - and if it is not released then noone else can acquire
// the lock - so we inherit from CriticalFinalizerObject which means that the
// the lock - so we inherit from CriticalFinalizerObject which means that the
// finalizer "should" run in all situations - there is always a chance that it
// does not run and the semaphore remains "acquired" but then chances are the
// whole process (w3wp.exe...) is going down, at which point the semaphore will
@@ -200,4 +200,4 @@ namespace Umbraco.Core
}
}
}
}
}

View File

@@ -1,126 +1,166 @@
using System;
namespace Umbraco.Core
{
/// <summary>
/// Provides ways to create attempts.
/// </summary>
public static class Attempt
{
// note:
// cannot rely on overloads only to differenciate between with/without status
// in some cases it will always be ambiguous, so be explicit w/ 'WithStatus' methods
/// <summary>
/// Creates a successful attempt with a result.
/// </summary>
/// <typeparam name="TResult">The type of the attempted operation result.</typeparam>
/// <param name="result">The result of the attempt.</param>
/// <returns>The successful attempt.</returns>
public static Attempt<TResult> Succeed<TResult>(TResult result)
{
return Attempt<TResult>.Succeed(result);
}
/// <summary>
/// Creates a successful attempt with a result and a status.
/// </summary>
/// <typeparam name="TResult">The type of the attempted operation result.</typeparam>
/// <typeparam name="TStatus">The type of the attempted operation status.</typeparam>
/// <param name="status">The status of the attempt.</param>
/// <param name="result">The result of the attempt.</param>
/// <returns>The successful attempt.</returns>
public static Attempt<TResult, TStatus> SucceedWithStatus<TResult, TStatus>(TStatus status, TResult result)
{
return Attempt<TResult, TStatus>.Succeed(status, result);
}
/// <summary>
/// Creates a failed attempt.
/// </summary>
/// <typeparam name="TResult">The type of the attempted operation result.</typeparam>
/// <returns>The failed attempt.</returns>
public static Attempt<TResult> Fail<TResult>()
{
return Attempt<TResult>.Fail();
}
/// <summary>
/// Creates a failed attempt with a result.
/// </summary>
/// <typeparam name="TResult">The type of the attempted operation result.</typeparam>
/// <param name="result">The result of the attempt.</param>
/// <returns>The failed attempt.</returns>
public static Attempt<TResult> Fail<TResult>(TResult result)
{
return Attempt<TResult>.Fail(result);
}
/// <summary>
/// Creates a failed attempt with a result and a status.
/// </summary>
/// <typeparam name="TResult">The type of the attempted operation result.</typeparam>
/// <typeparam name="TStatus">The type of the attempted operation status.</typeparam>
/// <param name="status">The status of the attempt.</param>
/// <param name="result">The result of the attempt.</param>
/// <returns>The failed attempt.</returns>
public static Attempt<TResult, TStatus> FailWithStatus<TResult, TStatus>(TStatus status, TResult result)
{
return Attempt<TResult, TStatus>.Fail(status, result);
}
/// <summary>
/// Creates a failed attempt with a result and an exception.
/// </summary>
/// <typeparam name="TResult">The type of the attempted operation result.</typeparam>
/// <param name="result">The result of the attempt.</param>
/// <param name="exception">The exception causing the failure of the attempt.</param>
/// <returns>The failed attempt.</returns>
public static Attempt<TResult> Fail<TResult>(TResult result, Exception exception)
{
return Attempt<TResult>.Fail(result, exception);
}
/// <summary>
/// Creates a failed attempt with a result, an exception and a status.
/// </summary>
/// <typeparam name="TResult">The type of the attempted operation result.</typeparam>
/// <typeparam name="TStatus">The type of the attempted operation status.</typeparam>
/// <param name="status">The status of the attempt.</param>
/// <param name="result">The result of the attempt.</param>
/// <param name="exception">The exception causing the failure of the attempt.</param>
/// <returns>The failed attempt.</returns>
public static Attempt<TResult, TStatus> FailWithStatus<TResult, TStatus>(TStatus status, TResult result, Exception exception)
{
return Attempt<TResult, TStatus>.Fail(status, result, exception);
}
/// <summary>
/// Creates a successful or a failed attempt, with a result.
/// </summary>
/// <typeparam name="TResult">The type of the attempted operation result.</typeparam>
/// <param name="condition">A value indicating whether the attempt is successful.</param>
/// <param name="result">The result of the attempt.</param>
/// <returns>The attempt.</returns>
public static Attempt<TResult> If<TResult>(bool condition, TResult result)
{
return Attempt<TResult>.If(condition, result);
}
/// <summary>
/// Creates a successful or a failed attempt, with a result.
/// </summary>
/// <typeparam name="TResult">The type of the attempted operation result.</typeparam>
/// <typeparam name="TStatus">The type of the attempted operation status.</typeparam>
/// <param name="condition">A value indicating whether the attempt is successful.</param>
/// <param name="succStatus">The status of the successful attempt.</param>
/// <param name="failStatus">The status of the failed attempt.</param>
/// <param name="result">The result of the attempt.</param>
/// <returns>The attempt.</returns>
public static Attempt<TResult, TStatus> IfWithStatus<TResult, TStatus>(bool condition, TStatus succStatus, TStatus failStatus, TResult result)
{
return Attempt<TResult, TStatus>.If(condition, succStatus, failStatus, result);
}
}
}
using System;
namespace Umbraco.Core
{
/// <summary>
/// Provides ways to create attempts.
/// </summary>
public static class Attempt
{
/// <summary>
/// Creates a successful attempt with a result.
/// </summary>
/// <typeparam name="T">The type of the attempted operation result.</typeparam>
/// <param name="result">The result of the attempt.</param>
/// <returns>The successful attempt.</returns>
public static Attempt<T> Succeed<T>(T result)
{
return Attempt<T>.Succeed(result);
}
/// <summary>
/// Creates a failed attempt with a result.
/// </summary>
/// <typeparam name="T">The type of the attempted operation result.</typeparam>
/// <param name="result">The result of the attempt.</param>
/// <returns>The failed attempt.</returns>
public static Attempt<T> Fail<T>(T result)
{
return Attempt<T>.Fail(result);
}
/// <summary>
/// Creates a failed attempt with a result and an exception.
/// </summary>
/// <typeparam name="T">The type of the attempted operation result.</typeparam>
/// <param name="result">The result of the attempt.</param>
/// <param name="exception">The exception causing the failure of the attempt.</param>
/// <returns>The failed attempt.</returns>
public static Attempt<T> Fail<T>(T result, Exception exception)
{
return Attempt<T>.Fail(result, exception);
}
/// <summary>
/// Creates a successful or a failed attempt, with a result.
/// </summary>
/// <typeparam name="T">The type of the attempted operation result.</typeparam>
/// <param name="success">A value indicating whether the attempt is successful.</param>
/// <param name="result">The result of the attempt.</param>
/// <returns>The attempt.</returns>
public static Attempt<T> If<T>(bool success, T result)
{
return Attempt<T>.SucceedIf(success, result);
}
/// <summary>
/// Executes an attempt function, with callbacks.
/// </summary>
/// <typeparam name="T">The type of the attempted operation result.</typeparam>
/// <param name="attempt">The attempt returned by the attempt function.</param>
/// <param name="onSuccess">An action to execute in case the attempt succeeds.</param>
/// <param name="onFail">An action to execute in case the attempt fails.</param>
/// <returns>The outcome of the attempt.</returns>
/// <remarks>Runs <paramref name="onSuccess"/> or <paramref name="onFail"/> depending on the
/// whether the attempt function reports a success or a failure.</remarks>
public static Outcome Try<T>(Attempt<T> attempt, Action<T> onSuccess, Action<Exception> onFail = null)
{
if (attempt.Success)
{
onSuccess(attempt.Result);
return Outcome.Success;
}
if (onFail != null)
{
onFail(attempt.Exception);
}
return Outcome.Failure;
}
/// <summary>
/// Represents the outcome of an attempt.
/// </summary>
/// <remarks>Can be a success or a failure, and allows for attempts chaining.</remarks>
public struct Outcome
{
private readonly bool _success;
/// <summary>
/// Gets an outcome representing a success.
/// </summary>
public static readonly Outcome Success = new Outcome(true);
/// <summary>
/// Gets an outcome representing a failure.
/// </summary>
public static readonly Outcome Failure = new Outcome(false);
private Outcome(bool success)
{
_success = success;
}
/// <summary>
/// Executes another attempt function, if the previous one failed, with callbacks.
/// </summary>
/// <typeparam name="T">The type of the attempted operation result.</typeparam>
/// <param name="nextFunction">The attempt function to execute, returning an attempt.</param>
/// <param name="onSuccess">An action to execute in case the attempt succeeds.</param>
/// <param name="onFail">An action to execute in case the attempt fails.</param>
/// <returns>If it executes, returns the outcome of the attempt, else returns a success outcome.</returns>
/// <remarks>
/// <para>Executes only if the previous attempt failed, else does not execute and return a success outcome.</para>
/// <para>If it executes, then runs <paramref name="onSuccess"/> or <paramref name="onFail"/> depending on the
/// whether the attempt function reports a success or a failure.</para>
/// </remarks>
public Outcome OnFailure<T>(Func<Attempt<T>> nextFunction, Action<T> onSuccess, Action<Exception> onFail = null)
{
return _success
? Success
: ExecuteNextFunction(nextFunction, onSuccess, onFail);
}
/// <summary>
/// Executes another attempt function, if the previous one succeeded, with callbacks.
/// </summary>
/// <typeparam name="T">The type of the attempted operation result.</typeparam>
/// <param name="nextFunction">The attempt function to execute, returning an attempt.</param>
/// <param name="onSuccess">An action to execute in case the attempt succeeds.</param>
/// <param name="onFail">An action to execute in case the attempt fails.</param>
/// <returns>If it executes, returns the outcome of the attempt, else returns a failed outcome.</returns>
/// <remarks>
/// <para>Executes only if the previous attempt succeeded, else does not execute and return a success outcome.</para>
/// <para>If it executes, then runs <paramref name="onSuccess"/> or <paramref name="onFail"/> depending on the
/// whether the attempt function reports a success or a failure.</para>
/// </remarks>
public Outcome OnSuccess<T>(Func<Attempt<T>> nextFunction, Action<T> onSuccess, Action<Exception> onFail = null)
{
return _success
? ExecuteNextFunction(nextFunction, onSuccess, onFail)
: Failure;
}
private static Outcome ExecuteNextFunction<T>(Func<Attempt<T>> nextFunction, Action<T> onSuccess, Action<Exception> onFail = null)
{
var attempt = nextFunction();
if (attempt.Success)
{
onSuccess(attempt.Result);
return Success;
}
if (onFail != null)
{
onFail(attempt.Exception);
}
return Failure;
}
}
}
}

View File

@@ -1,133 +0,0 @@
using System;
namespace Umbraco.Core
{
/// <summary>
/// Represents the result of an operation attempt.
/// </summary>
/// <typeparam name="TResult">The type of the attempted operation result.</typeparam>
[Serializable]
public struct Attempt<TResult>
{
// private - use Succeed() or Fail() methods to create attempts
private Attempt(bool success, TResult result, Exception exception)
{
Success = success;
Result = result;
Exception = exception;
}
/// <summary>
/// Gets a value indicating whether this <see cref="Attempt{TResult}"/> was successful.
/// </summary>
public bool Success { get; }
/// <summary>
/// Gets the exception associated with an unsuccessful attempt.
/// </summary>
public Exception Exception { get; }
/// <summary>
/// Gets the attempt result.
/// </summary>
public TResult Result { get; }
/// <summary>
/// Gets the attempt result, if successful, else a default value.
/// </summary>
public TResult ResultOr(TResult value) => Success ? Result : value;
// optimize, use a singleton failed attempt
private static readonly Attempt<TResult> Failed = new Attempt<TResult>(false, default(TResult), null);
/// <summary>
/// Creates a successful attempt.
/// </summary>
/// <returns>The successful attempt.</returns>
public static Attempt<TResult> Succeed()
{
return new Attempt<TResult>(true, default(TResult), null);
}
/// <summary>
/// Creates a successful attempt with a result.
/// </summary>
/// <param name="result">The result of the attempt.</param>
/// <returns>The successful attempt.</returns>
public static Attempt<TResult> Succeed(TResult result)
{
return new Attempt<TResult>(true, result, null);
}
/// <summary>
/// Creates a failed attempt.
/// </summary>
/// <returns>The failed attempt.</returns>
public static Attempt<TResult> Fail()
{
return Failed;
}
/// <summary>
/// Creates a failed attempt with an exception.
/// </summary>
/// <param name="exception">The exception causing the failure of the attempt.</param>
/// <returns>The failed attempt.</returns>
public static Attempt<TResult> Fail(Exception exception)
{
return new Attempt<TResult>(false, default(TResult), exception);
}
/// <summary>
/// Creates a failed attempt with a result.
/// </summary>
/// <param name="result">The result of the attempt.</param>
/// <returns>The failed attempt.</returns>
public static Attempt<TResult> Fail(TResult result)
{
return new Attempt<TResult>(false, result, null);
}
/// <summary>
/// Creates a failed attempt with a result and an exception.
/// </summary>
/// <param name="result">The result of the attempt.</param>
/// <param name="exception">The exception causing the failure of the attempt.</param>
/// <returns>The failed attempt.</returns>
public static Attempt<TResult> Fail(TResult result, Exception exception)
{
return new Attempt<TResult>(false, result, exception);
}
/// <summary>
/// Creates a successful or a failed attempt.
/// </summary>
/// <param name="condition">A value indicating whether the attempt is successful.</param>
/// <returns>The attempt.</returns>
public static Attempt<TResult> If(bool condition)
{
return condition ? new Attempt<TResult>(true, default(TResult), null) : Failed;
}
/// <summary>
/// Creates a successful or a failed attempt, with a result.
/// </summary>
/// <param name="condition">A value indicating whether the attempt is successful.</param>
/// <param name="result">The result of the attempt.</param>
/// <returns>The attempt.</returns>
public static Attempt<TResult> If(bool condition, TResult result)
{
return new Attempt<TResult>(condition, result, null);
}
/// <summary>
/// Implicity operator to check if the attempt was successful without having to access the 'success' property
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
public static implicit operator bool(Attempt<TResult> a)
{
return a.Success;
}
}
}

View File

@@ -1,142 +0,0 @@
using System;
namespace Umbraco.Core
{
/// <summary>
/// Represents the result of an operation attempt.
/// </summary>
/// <typeparam name="TResult">The type of the attempted operation result.</typeparam>
/// <typeparam name="TStatus">The type of the attempted operation status.</typeparam>
[Serializable]
public struct Attempt<TResult, TStatus>
{
/// <summary>
/// Gets a value indicating whether this <see cref="Attempt{TResult,TStatus}"/> was successful.
/// </summary>
public bool Success { get; }
/// <summary>
/// Gets the exception associated with an unsuccessful attempt.
/// </summary>
public Exception Exception { get; }
/// <summary>
/// Gets the attempt result.
/// </summary>
public TResult Result { get; }
/// <summary>
/// Gets the attempt status.
/// </summary>
public TStatus Status { get; }
// private - use Succeed() or Fail() methods to create attempts
private Attempt(bool success, TResult result, TStatus status, Exception exception)
{
Success = success;
Result = result;
Status = status;
Exception = exception;
}
/// <summary>
/// Creates a successful attempt.
/// </summary>
/// <param name="status">The status of the attempt.</param>
/// <returns>The successful attempt.</returns>
public static Attempt<TResult, TStatus> Succeed(TStatus status)
{
return new Attempt<TResult, TStatus>(true, default(TResult), status, null);
}
/// <summary>
/// Creates a successful attempt with a result.
/// </summary>
/// <param name="status">The status of the attempt.</param>
/// <param name="result">The result of the attempt.</param>
/// <returns>The successful attempt.</returns>
public static Attempt<TResult, TStatus> Succeed(TStatus status, TResult result)
{
return new Attempt<TResult, TStatus>(true, result, status, null);
}
/// <summary>
/// Creates a failed attempt.
/// </summary>
/// <param name="status">The status of the attempt.</param>
/// <returns>The failed attempt.</returns>
public static Attempt<TResult, TStatus> Fail(TStatus status)
{
return new Attempt<TResult, TStatus>(false, default(TResult), status, null);
}
/// <summary>
/// Creates a failed attempt with an exception.
/// </summary>
/// <param name="status">The status of the attempt.</param>
/// <param name="exception">The exception causing the failure of the attempt.</param>
/// <returns>The failed attempt.</returns>
public static Attempt<TResult, TStatus> Fail(TStatus status, Exception exception)
{
return new Attempt<TResult, TStatus>(false, default(TResult), status, exception);
}
/// <summary>
/// Creates a failed attempt with a result.
/// </summary>
/// <param name="status">The status of the attempt.</param>
/// <param name="result">The result of the attempt.</param>
/// <returns>The failed attempt.</returns>
public static Attempt<TResult, TStatus> Fail(TStatus status, TResult result)
{
return new Attempt<TResult, TStatus>(false, result, status, null);
}
/// <summary>
/// Creates a failed attempt with a result and an exception.
/// </summary>
/// <param name="status">The status of the attempt.</param>
/// <param name="result">The result of the attempt.</param>
/// <param name="exception">The exception causing the failure of the attempt.</param>
/// <returns>The failed attempt.</returns>
public static Attempt<TResult, TStatus> Fail(TStatus status, TResult result, Exception exception)
{
return new Attempt<TResult, TStatus>(false, result, status, exception);
}
/// <summary>
/// Creates a successful or a failed attempt.
/// </summary>
/// <param name="condition">A value indicating whether the attempt is successful.</param>
/// <param name="succStatus">The status of the successful attempt.</param>
/// <param name="failStatus">The status of the failed attempt.</param>
/// <returns>The attempt.</returns>
public static Attempt<TResult, TStatus> If(bool condition, TStatus succStatus, TStatus failStatus)
{
return new Attempt<TResult, TStatus>(condition, default(TResult), condition ? succStatus : failStatus, null);
}
/// <summary>
/// Creates a successful or a failed attempt, with a result.
/// </summary>
/// <param name="condition">A value indicating whether the attempt is successful.</param>
/// <param name="succStatus">The status of the successful attempt.</param>
/// <param name="failStatus">The status of the failed attempt.</param>
/// <param name="result">The result of the attempt.</param>
/// <returns>The attempt.</returns>
public static Attempt<TResult, TStatus> If(bool condition, TStatus succStatus, TStatus failStatus, TResult result)
{
return new Attempt<TResult, TStatus>(condition, result, condition ? succStatus : failStatus, null);
}
/// <summary>
/// Implicity operator to check if the attempt was successful without having to access the 'success' property
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
public static implicit operator bool(Attempt<TResult, TStatus> a)
{
return a.Success;
}
}
}

View File

@@ -0,0 +1,174 @@
using System;
using Umbraco.Core.Dynamics;
namespace Umbraco.Core
{
/// <summary>
/// Represents the result of an operation attempt.
/// </summary>
/// <typeparam name="T">The type of the attempted operation result.</typeparam>
[Serializable]
public struct Attempt<T>
{
private readonly bool _success;
private readonly T _result;
private readonly Exception _exception;
/// <summary>
/// Gets a value indicating whether this <see cref="Attempt{T}"/> was successful.
/// </summary>
public bool Success
{
get { return _success; }
}
/// <summary>
/// Gets the exception associated with an unsuccessful attempt.
/// </summary>
public Exception Exception { get { return _exception; } }
/// <summary>
/// Gets the exception associated with an unsuccessful attempt.
/// </summary>
/// <remarks>Keep it for backward compatibility sake.</remarks>
[Obsolete(".Error is obsolete, you should use .Exception instead.", false)]
public Exception Error { get { return _exception; } }
/// <summary>
/// Gets the attempt result.
/// </summary>
public T Result
{
get { return _result; }
}
// optimize, use a singleton failed attempt
private static readonly Attempt<T> Failed = new Attempt<T>(false, default(T), null);
/// <summary>
/// Represents an unsuccessful attempt.
/// </summary>
/// <remarks>Keep it for backward compatibility sake.</remarks>
[Obsolete(".Failed is obsolete, you should use Attempt<T>.Fail() instead.", false)]
public static readonly Attempt<T> False = Failed;
// private - use Succeed() or Fail() methods to create attempts
private Attempt(bool success, T result, Exception exception)
{
_success = success;
_result = result;
_exception = exception;
}
/// <summary>
/// Initialize a new instance of the <see cref="Attempt{T}"/> struct with a result.
/// </summary>
/// <param name="success">A value indicating whether the attempt is successful.</param>
/// <param name="result">The result of the attempt.</param>
/// <remarks>Keep it for backward compatibility sake.</remarks>
[Obsolete("Attempt ctors are obsolete, you should use Attempt<T>.Succeed(), Attempt<T>.Fail() or Attempt<T>.If() instead.", false)]
public Attempt(bool success, T result)
: this(success, result, null)
{ }
/// <summary>
/// Initialize a new instance of the <see cref="Attempt{T}"/> struct representing a failed attempt, with an exception.
/// </summary>
/// <param name="exception">The exception causing the failure of the attempt.</param>
/// <remarks>Keep it for backward compatibility sake.</remarks>
[Obsolete("Attempt ctors are obsolete, you should use Attempt<T>.Succeed(), Attempt<T>.Fail() or Attempt<T>.If() instead.", false)]
public Attempt(Exception exception)
: this(false, default(T), exception)
{ }
/// <summary>
/// Creates a successful attempt.
/// </summary>
/// <returns>The successful attempt.</returns>
public static Attempt<T> Succeed()
{
return new Attempt<T>(true, default(T), null);
}
/// <summary>
/// Creates a successful attempt with a result.
/// </summary>
/// <param name="result">The result of the attempt.</param>
/// <returns>The successful attempt.</returns>
public static Attempt<T> Succeed(T result)
{
return new Attempt<T>(true, result, null);
}
/// <summary>
/// Creates a failed attempt.
/// </summary>
/// <returns>The failed attempt.</returns>
public static Attempt<T> Fail()
{
return Failed;
}
/// <summary>
/// Creates a failed attempt with an exception.
/// </summary>
/// <param name="exception">The exception causing the failure of the attempt.</param>
/// <returns>The failed attempt.</returns>
public static Attempt<T> Fail(Exception exception)
{
return new Attempt<T>(false, default(T), exception);
}
/// <summary>
/// Creates a failed attempt with a result.
/// </summary>
/// <param name="result">The result of the attempt.</param>
/// <returns>The failed attempt.</returns>
public static Attempt<T> Fail(T result)
{
return new Attempt<T>(false, result, null);
}
/// <summary>
/// Creates a failed attempt with a result and an exception.
/// </summary>
/// <param name="result">The result of the attempt.</param>
/// <param name="exception">The exception causing the failure of the attempt.</param>
/// <returns>The failed attempt.</returns>
public static Attempt<T> Fail(T result, Exception exception)
{
return new Attempt<T>(false, result, exception);
}
/// <summary>
/// Creates a successful or a failed attempt.
/// </summary>
/// <param name="condition">A value indicating whether the attempt is successful.</param>
/// <returns>The attempt.</returns>
public static Attempt<T> SucceedIf(bool condition)
{
return condition ? new Attempt<T>(true, default(T), null) : Failed;
}
/// <summary>
/// Creates a successful or a failed attempt, with a result.
/// </summary>
/// <param name="condition">A value indicating whether the attempt is successful.</param>
/// <param name="result">The result of the attempt.</param>
/// <returns>The attempt.</returns>
public static Attempt<T> SucceedIf(bool condition, T result)
{
return new Attempt<T>(condition, result, null);
}
/// <summary>
/// Implicity operator to check if the attempt was successful without having to access the 'success' property
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
public static implicit operator bool(Attempt<T> a)
{
return a.Success;
}
}
}

View File

@@ -0,0 +1,17 @@
using System;
using System.ComponentModel;
namespace Umbraco.Core.Auditing
{
[Obsolete("Use Umbraco.Core.Services.IAuditService instead")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static class Audit
{
public static void Add(Umbraco.Core.Auditing.AuditTypes type, string comment, int userId, int objectId)
{
ApplicationContext.Current.Services.AuditService.Add(
Enum<Umbraco.Core.Models.AuditType>.Parse(type.ToString()),
comment, userId, objectId);
}
}
}

View File

@@ -0,0 +1,351 @@
using System;
using System.Linq;
using System.Text;
using System.Threading;
using System.Web;
using Umbraco.Core.Events;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
namespace Umbraco.Core.Auditing
{
public sealed class AuditEventHandler : ApplicationEventHandler
{
private IAuditService _auditServiceInstance;
private IUserService _userServiceInstance;
private IEntityService _entityServiceInstance;
private IUser CurrentPerformingUser
{
get
{
var identity = Thread.CurrentPrincipal?.GetUmbracoIdentity();
return identity == null
? new User { Id = 0, Name = "SYSTEM", Email = "" }
: _userServiceInstance.GetUserById(Convert.ToInt32(identity.Id));
}
}
private IUser GetPerformingUser(int userId)
{
var found = userId >= 0 ? _userServiceInstance.GetUserById(userId) : null;
return found ?? new User {Id = 0, Name = "SYSTEM", Email = ""};
}
private string PerformingIp
{
get
{
var httpContext = HttpContext.Current == null ? (HttpContextBase) null : new HttpContextWrapper(HttpContext.Current);
var ip = httpContext.GetCurrentRequestIpAddress();
if (ip.ToLowerInvariant().StartsWith("unknown")) ip = "";
return ip;
}
}
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
_auditServiceInstance = applicationContext.Services.AuditService;
_userServiceInstance = applicationContext.Services.UserService;
_entityServiceInstance = applicationContext.Services.EntityService;
//BackOfficeUserManager.AccountLocked += ;
//BackOfficeUserManager.AccountUnlocked += ;
BackOfficeUserManager.ForgotPasswordRequested += OnForgotPasswordRequest;
BackOfficeUserManager.ForgotPasswordChangedSuccess += OnForgotPasswordChange;
BackOfficeUserManager.LoginFailed += OnLoginFailed;
//BackOfficeUserManager.LoginRequiresVerification += ;
BackOfficeUserManager.LoginSuccess += OnLoginSuccess;
BackOfficeUserManager.LogoutSuccess += OnLogoutSuccess;
BackOfficeUserManager.PasswordChanged += OnPasswordChanged;
BackOfficeUserManager.PasswordReset += OnPasswordReset;
//BackOfficeUserManager.ResetAccessFailedCount += ;
UserService.SavedUserGroup2 += OnSavedUserGroupWithUsers;
UserService.SavedUser += OnSavedUser;
UserService.DeletedUser += OnDeletedUser;
UserService.UserGroupPermissionsAssigned += UserGroupPermissionAssigned;
MemberService.Saved += OnSavedMember;
MemberService.Deleted += OnDeletedMember;
MemberService.AssignedRoles += OnAssignedRoles;
MemberService.RemovedRoles += OnRemovedRoles;
MemberService.Exported += OnMemberExported;
}
private string FormatEmail(IMember member)
{
return member == null ? string.Empty : member.Email.IsNullOrWhiteSpace() ? "" : $"<{member.Email}>";
}
private string FormatEmail(IUser user)
{
return user == null ? string.Empty : user.Email.IsNullOrWhiteSpace() ? "" : $"<{user.Email}>";
}
private void OnRemovedRoles(IMemberService sender, RolesEventArgs args)
{
var performingUser = CurrentPerformingUser;
var roles = string.Join(", ", args.Roles);
var members = sender.GetAllMembers(args.MemberIds).ToDictionary(x => x.Id, x => x);
foreach (var id in args.MemberIds)
{
members.TryGetValue(id, out var member);
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
DateTime.UtcNow,
-1, $"Member {id} \"{member?.Name ?? "(unknown)"}\" {FormatEmail(member)}",
"umbraco/member/roles/removed", $"roles modified, removed {roles}");
}
}
private void OnAssignedRoles(IMemberService sender, RolesEventArgs args)
{
var performingUser = CurrentPerformingUser;
var roles = string.Join(", ", args.Roles);
var members = sender.GetAllMembers(args.MemberIds).ToDictionary(x => x.Id, x => x);
foreach (var id in args.MemberIds)
{
members.TryGetValue(id, out var member);
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
DateTime.UtcNow,
-1, $"Member {id} \"{member?.Name ?? "(unknown)"}\" {FormatEmail(member)}",
"umbraco/member/roles/assigned", $"roles modified, assigned {roles}");
}
}
private void OnMemberExported(IMemberService sender, ExportedMemberEventArgs exportedMemberEventArgs)
{
var performingUser = CurrentPerformingUser;
var member = exportedMemberEventArgs.Member;
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
DateTime.UtcNow,
-1, $"Member {member.Id} \"{member.Name}\" {FormatEmail(member)}",
"umbraco/member/exported", "exported member data");
}
private void OnSavedUserGroupWithUsers(IUserService sender, SaveEventArgs<UserGroupWithUsers> saveEventArgs)
{
var performingUser = CurrentPerformingUser;
foreach (var groupWithUser in saveEventArgs.SavedEntities)
{
var group = groupWithUser.UserGroup;
var dp = string.Join(", ", ((UserGroup)group).GetPreviouslyDirtyProperties());
var sections = ((UserGroup)group).WasPropertyDirty("AllowedSections")
? string.Join(", ", group.AllowedSections)
: null;
var perms = ((UserGroup)group).WasPropertyDirty("Permissions")
? string.Join(", ", group.Permissions)
: null;
var sb = new StringBuilder();
sb.Append($"updating {(string.IsNullOrWhiteSpace(dp) ? "(nothing)" : dp)};");
if (sections != null)
sb.Append($", assigned sections: {sections}");
if (perms != null)
{
if (sections != null)
sb.Append(", ");
sb.Append($"default perms: {perms}");
}
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
DateTime.UtcNow,
-1, $"User Group {group.Id} \"{group.Name}\" ({group.Alias})",
"umbraco/user-group/save", $"{sb}");
// now audit the users that have changed
foreach (var user in groupWithUser.RemovedUsers)
{
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
DateTime.UtcNow,
user.Id, $"User \"{user.Name}\" {FormatEmail(user)}",
"umbraco/user-group/save", $"Removed user \"{user.Name}\" {FormatEmail(user)} from group {group.Id} \"{group.Name}\" ({group.Alias})");
}
foreach (var user in groupWithUser.AddedUsers)
{
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
DateTime.UtcNow,
user.Id, $"User \"{user.Name}\" {FormatEmail(user)}",
"umbraco/user-group/save", $"Added user \"{user.Name}\" {FormatEmail(user)} to group {group.Id} \"{group.Name}\" ({group.Alias})");
}
}
}
private void UserGroupPermissionAssigned(IUserService sender, SaveEventArgs<EntityPermission> saveEventArgs)
{
var performingUser = CurrentPerformingUser;
var perms = saveEventArgs.SavedEntities;
foreach (var perm in perms)
{
var group = sender.GetUserGroupById(perm.UserGroupId);
var assigned = string.Join(", ", perm.AssignedPermissions);
var entity = _entityServiceInstance.Get(perm.EntityId);
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
DateTime.UtcNow,
-1, $"User Group {group.Id} \"{group.Name}\" ({group.Alias})",
"umbraco/user-group/permissions-change", $"assigning {(string.IsNullOrWhiteSpace(assigned) ? "(nothing)" : assigned)} on id:{perm.EntityId} \"{entity.Name}\"");
}
}
private void OnSavedMember(IMemberService sender, SaveEventArgs<IMember> saveEventArgs)
{
var performingUser = CurrentPerformingUser;
var members = saveEventArgs.SavedEntities;
foreach (var member in members)
{
var dp = string.Join(", ", ((Member) member).GetPreviouslyDirtyProperties());
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
DateTime.UtcNow,
-1, $"Member {member.Id} \"{member.Name}\" {FormatEmail(member)}",
"umbraco/member/save", $"updating {(string.IsNullOrWhiteSpace(dp) ? "(nothing)" : dp)}");
}
}
private void OnDeletedMember(IMemberService sender, DeleteEventArgs<IMember> deleteEventArgs)
{
var performingUser = CurrentPerformingUser;
var members = deleteEventArgs.DeletedEntities;
foreach (var member in members)
{
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
DateTime.UtcNow,
-1, $"Member {member.Id} \"{member.Name}\" {FormatEmail(member)}",
"umbraco/member/delete", $"delete member id:{member.Id} \"{member.Name}\" {FormatEmail(member)}");
}
}
private void OnSavedUser(IUserService sender, SaveEventArgs<IUser> saveEventArgs)
{
var performingUser = CurrentPerformingUser;
var affectedUsers = saveEventArgs.SavedEntities;
foreach (var affectedUser in affectedUsers)
{
var groups = affectedUser.WasPropertyDirty("Groups")
? string.Join(", ", affectedUser.Groups.Select(x => x.Alias))
: null;
var dp = string.Join(", ", ((User)affectedUser).GetPreviouslyDirtyProperties());
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
DateTime.UtcNow,
affectedUser.Id, $"User \"{affectedUser.Name}\" {FormatEmail(affectedUser)}",
"umbraco/user/save", $"updating {(string.IsNullOrWhiteSpace(dp) ? "(nothing)" : dp)}{(groups == null ? "" : "; groups assigned: " + groups)}");
}
}
private void OnDeletedUser(IUserService sender, DeleteEventArgs<IUser> deleteEventArgs)
{
var performingUser = CurrentPerformingUser;
var affectedUsers = deleteEventArgs.DeletedEntities;
foreach (var affectedUser in affectedUsers)
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
DateTime.UtcNow,
affectedUser.Id, $"User \"{affectedUser.Name}\" {FormatEmail(affectedUser)}",
"umbraco/user/delete", "delete user");
}
private void OnLoginSuccess(object sender, EventArgs args)
{
if (args is IdentityAuditEventArgs identityArgs)
{
var performingUser = GetPerformingUser(identityArgs.PerformingUser);
WriteAudit(performingUser, identityArgs.AffectedUser, identityArgs.IpAddress, "umbraco/user/sign-in/login", "login success");
}
}
private void OnLogoutSuccess(object sender, EventArgs args)
{
if (args is IdentityAuditEventArgs identityArgs)
{
var performingUser = GetPerformingUser(identityArgs.PerformingUser);
WriteAudit(performingUser, identityArgs.AffectedUser, identityArgs.IpAddress, "umbraco/user/sign-in/logout", "logout success");
}
}
private void OnPasswordReset(object sender, EventArgs args)
{
if (args is IdentityAuditEventArgs identityArgs && identityArgs.PerformingUser >= 0)
{
WriteAudit(identityArgs.PerformingUser, identityArgs.AffectedUser, identityArgs.IpAddress, "umbraco/user/password/reset", "password reset");
}
}
private void OnPasswordChanged(object sender, EventArgs args)
{
if (args is IdentityAuditEventArgs identityArgs && identityArgs.PerformingUser >= 0)
{
WriteAudit(identityArgs.PerformingUser, identityArgs.AffectedUser, identityArgs.IpAddress, "umbraco/user/password/change", "password change");
}
}
private void OnLoginFailed(object sender, EventArgs args)
{
if (args is IdentityAuditEventArgs identityArgs && identityArgs.PerformingUser >= 0)
{
WriteAudit(identityArgs.PerformingUser, 0, identityArgs.IpAddress, "umbraco/user/sign-in/failed", "login failed", affectedDetails: "");
}
}
private void OnForgotPasswordChange(object sender, EventArgs args)
{
if (args is IdentityAuditEventArgs identityArgs && identityArgs.PerformingUser >= 0)
{
WriteAudit(identityArgs.PerformingUser, identityArgs.AffectedUser, identityArgs.IpAddress, "umbraco/user/password/forgot/change", "password forgot/change");
}
}
private void OnForgotPasswordRequest(object sender, EventArgs args)
{
if (args is IdentityAuditEventArgs identityArgs && identityArgs.PerformingUser >= 0)
{
WriteAudit(identityArgs.PerformingUser, identityArgs.AffectedUser, identityArgs.IpAddress, "umbraco/user/password/forgot/request", "password forgot/request");
}
}
private void WriteAudit(int performingId, int affectedId, string ipAddress, string eventType, string eventDetails, string affectedDetails = null)
{
var performingUser = _userServiceInstance.GetUserById(performingId);
var performingDetails = performingUser == null
? $"User UNKNOWN:{performingId}"
: $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}";
WriteAudit(performingId, performingDetails, affectedId, ipAddress, eventType, eventDetails, affectedDetails);
}
private void WriteAudit(IUser performingUser, int affectedId, string ipAddress, string eventType, string eventDetails)
{
var performingDetails = performingUser == null
? $"User UNKNOWN"
: $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}";
WriteAudit(performingUser?.Id ?? 0, performingDetails, affectedId, ipAddress, eventType, eventDetails);
}
private void WriteAudit(int performingId, string performingDetails, int affectedId, string ipAddress, string eventType, string eventDetails, string affectedDetails = null)
{
if (affectedDetails == null)
{
var affectedUser = _userServiceInstance.GetUserById(affectedId);
affectedDetails = affectedUser == null
? $"User UNKNOWN:{affectedId}"
: $"User \"{affectedUser.Name}\" {FormatEmail(affectedUser)}";
}
_auditServiceInstance.Write(performingId, performingDetails,
ipAddress,
DateTime.UtcNow,
affectedId, affectedDetails,
eventType, eventDetails);
}
}
}

View File

@@ -0,0 +1,87 @@
using System;
using System.ComponentModel;
namespace Umbraco.Core.Auditing
{
[Obsolete("Use Umbraco.Core.Models.AuditType instead")]
[EditorBrowsable(EditorBrowsableState.Never)]
public enum AuditTypes
{
/// <summary>
/// Used when new nodes are added
/// </summary>
New,
/// <summary>
/// Used when nodes are saved
/// </summary>
Save,
/// <summary>
/// Used when nodes are opened
/// </summary>
Open,
/// <summary>
/// Used when nodes are deleted
/// </summary>
Delete,
/// <summary>
/// Used when nodes are published
/// </summary>
Publish,
/// <summary>
/// Used when nodes are send to publishing
/// </summary>
SendToPublish,
/// <summary>
/// Used when nodes are unpublished
/// </summary>
UnPublish,
/// <summary>
/// Used when nodes are moved
/// </summary>
Move,
/// <summary>
/// Used when nodes are copied
/// </summary>
Copy,
/// <summary>
/// Used when nodes are assígned a domain
/// </summary>
AssignDomain,
/// <summary>
/// Used when public access are changed for a node
/// </summary>
PublicAccess,
/// <summary>
/// Used when nodes are sorted
/// </summary>
Sort,
/// <summary>
/// Used when a notification are send to a user
/// </summary>
Notify,
/// <summary>
/// General system notification
/// </summary>
System,
/// <summary>
/// Used when a node's content is rolled back to a previous version
/// </summary>
RollBack,
/// <summary>
/// Used when a package is installed
/// </summary>
PackagerInstall,
/// <summary>
/// Used when a package is uninstalled
/// </summary>
PackagerUninstall,
/// <summary>
/// Used when a node is send to translation
/// </summary>
SendToTranslate,
/// <summary>
/// Use this log action for custom log messages that should be shown in the audit trail
/// </summary>
Custom
}
}

View File

@@ -1,8 +1,10 @@
using System;
using System.ComponentModel;
using System.Threading;
using System.Web;
using Umbraco.Core.Security;
namespace Umbraco.Web.Security
namespace Umbraco.Core.Auditing
{
/// <summary>
/// This class is used by events raised from hthe BackofficeUserManager
@@ -44,6 +46,19 @@ namespace Umbraco.Web.Security
/// </summary>
public string Username { get; private set; }
[Obsolete("Use the method that has the affectedUser parameter instead")]
[EditorBrowsable(EditorBrowsableState.Never)]
public IdentityAuditEventArgs(AuditEvent action, string ipAddress, int performingUser = -1)
{
DateTimeUtc = DateTime.UtcNow;
Action = action;
IpAddress = ipAddress;
PerformingUser = performingUser == -1
? GetCurrentRequestBackofficeUserId()
: performingUser;
}
/// <summary>
/// Default constructor

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Web;
@@ -18,6 +18,9 @@ namespace Umbraco.Core
// this only gets called when an assembly can't be resolved
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
}
private static readonly Regex Log4NetAssemblyPattern = new Regex("log4net, Version=([\\d\\.]+?), Culture=neutral, PublicKeyToken=\\w+$", RegexOptions.Compiled);
private const string Log4NetReplacement = "log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a";
/// <summary>
/// This is used to do an assembly binding redirect via code - normally required due to signature changes in assemblies
@@ -27,6 +30,12 @@ namespace Umbraco.Core
/// <returns></returns>
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
//log4net:
if (Log4NetAssemblyPattern.IsMatch(args.Name) && args.Name != Log4NetReplacement)
{
return Assembly.Load(Log4NetAssemblyPattern.Replace(args.Name, Log4NetReplacement));
}
//AutoMapper:
// ensure the assembly is indeed AutoMapper and that the PublicKeyToken is null before trying to Load again
// do NOT just replace this with 'return Assembly', as it will cause an infinite loop -> stackoverflow
@@ -34,7 +43,7 @@ namespace Umbraco.Core
return Assembly.Load(args.Name.Replace(", PublicKeyToken=null", ", PublicKeyToken=be96cd2c38ef1005"));
return null;
}
}
}
}

View File

@@ -1,99 +0,0 @@
using System;
using System.Web;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Class that is exposed by the ApplicationContext for application wide caching purposes
/// </summary>
public class CacheHelper
{
public static CacheHelper NoCache { get; } = new CacheHelper(NullCacheProvider.Instance, NullCacheProvider.Instance, NullCacheProvider.Instance, new IsolatedRuntimeCache(_ => NullCacheProvider.Instance));
/// <summary>
/// Creates a cache helper with disabled caches
/// </summary>
/// <returns></returns>
/// <remarks>
/// Good for unit testing
/// </remarks>
public static CacheHelper CreateDisabledCacheHelper()
{
// do *not* return NoCache
// NoCache is a special instance that is detected by RepositoryBase and disables all cache policies
// CreateDisabledCacheHelper is used in tests to use no cache, *but* keep all cache policies
return new CacheHelper(NullCacheProvider.Instance, NullCacheProvider.Instance, NullCacheProvider.Instance, new IsolatedRuntimeCache(_ => NullCacheProvider.Instance));
}
/// <summary>
/// Initializes a new instance for use in the web
/// </summary>
public CacheHelper()
: this(
new HttpRuntimeCacheProvider(HttpRuntime.Cache),
new StaticCacheProvider(),
new HttpRequestCacheProvider(),
new IsolatedRuntimeCache(t => new ObjectCacheRuntimeCacheProvider()))
{
}
/// <summary>
/// Initializes a new instance for use in the web
/// </summary>
/// <param name="cache"></param>
public CacheHelper(System.Web.Caching.Cache cache)
: this(
new HttpRuntimeCacheProvider(cache),
new StaticCacheProvider(),
new HttpRequestCacheProvider(),
new IsolatedRuntimeCache(t => new ObjectCacheRuntimeCacheProvider()))
{
}
/// <summary>
/// Initializes a new instance based on the provided providers
/// </summary>
/// <param name="httpCacheProvider"></param>
/// <param name="staticCacheProvider"></param>
/// <param name="requestCacheProvider"></param>
/// <param name="isolatedCacheManager"></param>
public CacheHelper(
IRuntimeCacheProvider httpCacheProvider,
ICacheProvider staticCacheProvider,
ICacheProvider requestCacheProvider,
IsolatedRuntimeCache isolatedCacheManager)
{
if (httpCacheProvider == null) throw new ArgumentNullException("httpCacheProvider");
if (staticCacheProvider == null) throw new ArgumentNullException("staticCacheProvider");
if (requestCacheProvider == null) throw new ArgumentNullException("requestCacheProvider");
if (isolatedCacheManager == null) throw new ArgumentNullException("isolatedCacheManager");
RuntimeCache = httpCacheProvider;
StaticCache = staticCacheProvider;
RequestCache = requestCacheProvider;
IsolatedRuntimeCache = isolatedCacheManager;
}
/// <summary>
/// Returns the current Request cache
/// </summary>
public ICacheProvider RequestCache { get; internal set; }
/// <summary>
/// Returns the current Runtime cache
/// </summary>
public ICacheProvider StaticCache { get; internal set; }
/// <summary>
/// Returns the current Runtime cache
/// </summary>
public IRuntimeCacheProvider RuntimeCache { get; internal set; }
/// <summary>
/// Returns the current Isolated Runtime cache manager
/// </summary>
public IsolatedRuntimeCache IsolatedRuntimeCache { get; internal set; }
}
}

View File

@@ -1,15 +1,97 @@
namespace Umbraco.Core.Cache
{
/// <summary>
/// Constants storing cache keys used in caching
/// </summary>
public static class CacheKeys
{
public const string ApplicationTreeCacheKey = "ApplicationTreeCache"; // used by ApplicationTreeService
public const string ApplicationsCacheKey = "ApplicationCache"; // used by SectionService
public const string TemplateFrontEndCacheKey = "template"; // fixme usage?
public const string MacroContentCacheKey = "macroContent_"; // used in MacroRenderers
}
}
using System;
using System.ComponentModel;
using Umbraco.Core.CodeAnnotations;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Constants storing cache keys used in caching
/// </summary>
public static class CacheKeys
{
public const string ApplicationTreeCacheKey = "ApplicationTreeCache";
public const string ApplicationsCacheKey = "ApplicationCache";
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
[EditorBrowsable(EditorBrowsableState.Never)]
public const string UserTypeCacheKey = "UserTypeCache";
[Obsolete("This is no longer used and will be removed from the codebase in the future - it is referenced but no cache is stored against this key")]
[EditorBrowsable(EditorBrowsableState.Never)]
public const string ContentItemCacheKey = "contentItem";
[UmbracoWillObsolete("This cache key is only used for the legacy 'library' caching, remove in v8")]
public const string MediaCacheKey = "UL_GetMedia";
public const string MacroXsltCacheKey = "macroXslt_";
[UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
public const string MacroCacheKey = "UmbracoMacroCache";
public const string MacroHtmlCacheKey = "macroHtml_";
public const string MacroControlCacheKey = "macroControl_";
public const string MacroHtmlDateAddedCacheKey = "macroHtml_DateAdded_";
public const string MacroControlDateAddedCacheKey = "macroControl_DateAdded_";
[UmbracoWillObsolete("This cache key is only used for legacy 'library' member caching, remove in v8")]
public const string MemberLibraryCacheKey = "UL_GetMember";
[UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
public const string MemberBusinessLogicCacheKey = "MemberCacheItem_";
[UmbracoWillObsolete("This cache key is only used for legacy template business logic caching, remove in v8")]
public const string TemplateFrontEndCacheKey = "template";
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
[EditorBrowsable(EditorBrowsableState.Never)]
public const string TemplateBusinessLogicCacheKey = "UmbracoTemplateCache";
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
[EditorBrowsable(EditorBrowsableState.Never)]
public const string UserContextCacheKey = "UmbracoUserContext";
public const string UserContextTimeoutCacheKey = "UmbracoUserContextTimeout";
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
[EditorBrowsable(EditorBrowsableState.Never)]
public const string UserCacheKey = "UmbracoUser";
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
[EditorBrowsable(EditorBrowsableState.Never)]
public const string UserGroupPermissionsCacheKey = "UmbracoUserGroupPermissions";
[UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
public const string ContentTypeCacheKey = "UmbracoContentType";
[UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
public const string ContentTypePropertiesCacheKey = "ContentType_PropertyTypes_Content:";
[Obsolete("No longer used and will be removed in v8")]
[EditorBrowsable(EditorBrowsableState.Never)]
public const string PropertyTypeCacheKey = "UmbracoPropertyTypeCache";
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
[EditorBrowsable(EditorBrowsableState.Never)]
public const string LanguageCacheKey = "UmbracoLanguageCache";
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
[EditorBrowsable(EditorBrowsableState.Never)]
public const string DomainCacheKey = "UmbracoDomainList";
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
[EditorBrowsable(EditorBrowsableState.Never)]
public const string StylesheetCacheKey = "UmbracoStylesheet";
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
[EditorBrowsable(EditorBrowsableState.Never)]
public const string StylesheetPropertyCacheKey = "UmbracoStylesheetProperty";
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
[EditorBrowsable(EditorBrowsableState.Never)]
public const string DataTypeCacheKey = "UmbracoDataTypeDefinition";
public const string DataTypePreValuesCacheKey = "UmbracoPreVal";
public const string IdToKeyCacheKey = "UI2K__";
public const string KeyToIdCacheKey = "UK2I__";
}
}

View File

@@ -1,70 +1,70 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Caching;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Extensions for strongly typed access
/// </summary>
public static class CacheProviderExtensions
{
public static T GetCacheItem<T>(this IRuntimeCacheProvider provider,
string cacheKey,
Func<T> getCacheItem,
TimeSpan? timeout,
bool isSliding = false,
CacheItemPriority priority = CacheItemPriority.Normal,
CacheItemRemovedCallback removedCallback = null,
string[] dependentFiles = null)
{
var result = provider.GetCacheItem(cacheKey, () => getCacheItem(), timeout, isSliding, priority, removedCallback, dependentFiles);
return result == null ? default(T) : result.TryConvertTo<T>().Result;
}
public static void InsertCacheItem<T>(this IRuntimeCacheProvider provider,
string cacheKey,
Func<T> getCacheItem,
TimeSpan? timeout = null,
bool isSliding = false,
CacheItemPriority priority = CacheItemPriority.Normal,
CacheItemRemovedCallback removedCallback = null,
string[] dependentFiles = null)
{
provider.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, isSliding, priority, removedCallback, dependentFiles);
}
public static IEnumerable<T> GetCacheItemsByKeySearch<T>(this ICacheProvider provider, string keyStartsWith)
{
var result = provider.GetCacheItemsByKeySearch(keyStartsWith);
return result.Select(x => x.TryConvertTo<T>().Result);
}
public static IEnumerable<T> GetCacheItemsByKeyExpression<T>(this ICacheProvider provider, string regexString)
{
var result = provider.GetCacheItemsByKeyExpression(regexString);
return result.Select(x => x.TryConvertTo<T>().Result);
}
public static T GetCacheItem<T>(this ICacheProvider provider, string cacheKey)
{
var result = provider.GetCacheItem(cacheKey);
if (result == null)
{
return default(T);
}
return result.TryConvertTo<T>().Result;
}
public static T GetCacheItem<T>(this ICacheProvider provider, string cacheKey, Func<T> getCacheItem)
{
var result = provider.GetCacheItem(cacheKey, () => getCacheItem());
if (result == null)
{
return default(T);
}
return result.TryConvertTo<T>().Result;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Caching;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Extensions for strongly typed access
/// </summary>
public static class CacheProviderExtensions
{
public static T GetCacheItem<T>(this IRuntimeCacheProvider provider,
string cacheKey,
Func<T> getCacheItem,
TimeSpan? timeout,
bool isSliding = false,
CacheItemPriority priority = CacheItemPriority.Normal,
CacheItemRemovedCallback removedCallback = null,
string[] dependentFiles = null)
{
var result = provider.GetCacheItem(cacheKey, () => getCacheItem(), timeout, isSliding, priority, removedCallback, dependentFiles);
return result == null ? default(T) : result.TryConvertTo<T>().Result;
}
public static void InsertCacheItem<T>(this IRuntimeCacheProvider provider,
string cacheKey,
Func<T> getCacheItem,
TimeSpan? timeout = null,
bool isSliding = false,
CacheItemPriority priority = CacheItemPriority.Normal,
CacheItemRemovedCallback removedCallback = null,
string[] dependentFiles = null)
{
provider.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, isSliding, priority, removedCallback, dependentFiles);
}
public static IEnumerable<T> GetCacheItemsByKeySearch<T>(this ICacheProvider provider, string keyStartsWith)
{
var result = provider.GetCacheItemsByKeySearch(keyStartsWith);
return result.Select(x => x.TryConvertTo<T>().Result);
}
public static IEnumerable<T> GetCacheItemsByKeyExpression<T>(this ICacheProvider provider, string regexString)
{
var result = provider.GetCacheItemsByKeyExpression(regexString);
return result.Select(x => x.TryConvertTo<T>().Result);
}
public static T GetCacheItem<T>(this ICacheProvider provider, string cacheKey)
{
var result = provider.GetCacheItem(cacheKey);
if (result == null)
{
return default(T);
}
return result.TryConvertTo<T>().Result;
}
public static T GetCacheItem<T>(this ICacheProvider provider, string cacheKey, Func<T> getCacheItem)
{
var result = provider.GetCacheItem(cacheKey, () => getCacheItem());
if (result == null)
{
return default(T);
}
return result.TryConvertTo<T>().Result;
}
}
}

View File

@@ -1,120 +1,78 @@
using System;
using Umbraco.Core.Events;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Sync;
namespace Umbraco.Core.Cache
{
/// <summary>
/// A base class for cache refreshers that handles events.
/// </summary>
/// <typeparam name="TInstanceType">The actual cache refresher type.</typeparam>
/// <remarks>The actual cache refresher type is used for strongly typed events.</remarks>
public abstract class CacheRefresherBase<TInstanceType> : ICacheRefresher
where TInstanceType : class, ICacheRefresher
{
/// <summary>
/// Initializes a new instance of the <see cref="CacheRefresherBase{TInstanceType}"/>.
/// </summary>
/// <param name="cacheHelper">A cache helper.</param>
protected CacheRefresherBase(CacheHelper cacheHelper)
{
CacheHelper = cacheHelper;
}
/// <summary>
/// Triggers when the cache is updated on the server.
/// </summary>
/// <remarks>
/// Triggers on each server configured for an Umbraco project whenever a cache refresher is updated.
/// </remarks>
public static event TypedEventHandler<TInstanceType, CacheRefresherEventArgs> CacheUpdated;
#region Define
/// <summary>
/// Gets the typed 'this' for events.
/// </summary>
protected abstract TInstanceType This { get; }
/// <summary>
/// Gets the unique identifier of the refresher.
/// </summary>
public abstract Guid RefresherUniqueId { get; }
/// <summary>
/// Gets the name of the refresher.
/// </summary>
public abstract string Name { get; }
#endregion
#region Refresher
/// <summary>
/// Refreshes all entities.
/// </summary>
public virtual void RefreshAll()
{
OnCacheUpdated(This, new CacheRefresherEventArgs(null, MessageType.RefreshAll));
}
/// <summary>
/// Refreshes an entity.
/// </summary>
/// <param name="id">The entity's identifier.</param>
public virtual void Refresh(int id)
{
OnCacheUpdated(This, new CacheRefresherEventArgs(id, MessageType.RefreshById));
}
/// <summary>
/// Refreshes an entity.
/// </summary>
/// <param name="id">The entity's identifier.</param>
public virtual void Refresh(Guid id)
{
OnCacheUpdated(This, new CacheRefresherEventArgs(id, MessageType.RefreshById));
}
/// <summary>
/// Removes an entity.
/// </summary>
/// <param name="id">The entity's identifier.</param>
public virtual void Remove(int id)
{
OnCacheUpdated(This, new CacheRefresherEventArgs(id, MessageType.RemoveById));
}
#endregion
#region Protected
/// <summary>
/// Gets the cache helper.
/// </summary>
protected CacheHelper CacheHelper { get; }
/// <summary>
/// Clears the cache for all repository entities of a specified type.
/// </summary>
/// <typeparam name="TEntity">The type of the entities.</typeparam>
protected void ClearAllIsolatedCacheByEntityType<TEntity>()
where TEntity : class, IEntity
{
CacheHelper.IsolatedRuntimeCache.ClearCache<TEntity>();
}
/// <summary>
/// Raises the CacheUpdated event.
/// </summary>
/// <param name="sender">The event sender.</param>
/// <param name="args">The event arguments.</param>
protected static void OnCacheUpdated(TInstanceType sender, CacheRefresherEventArgs args)
{
CacheUpdated?.Invoke(sender, args);
}
#endregion
}
}
using System;
using Umbraco.Core.Events;
using Umbraco.Core.Sync;
using umbraco.interfaces;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Cache
{
/// <summary>
/// A base class for cache refreshers to inherit from that ensures the correct events are raised
/// when cache refreshing occurs.
/// </summary>
/// <typeparam name="TInstanceType">The real cache refresher type, this is used for raising strongly typed events</typeparam>
public abstract class CacheRefresherBase<TInstanceType> : ICacheRefresher
where TInstanceType : ICacheRefresher
{
/// <summary>
/// An event that is raised when cache is updated on an individual server
/// </summary>
/// <remarks>
/// This event will fire on each server configured for an Umbraco project whenever a cache refresher
/// is updated.
/// </remarks>
public static event TypedEventHandler<TInstanceType, CacheRefresherEventArgs> CacheUpdated;
/// <summary>
/// Raises the event
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
protected static void OnCacheUpdated(TInstanceType sender, CacheRefresherEventArgs args)
{
if (CacheUpdated != null)
{
CacheUpdated(sender, args);
}
}
/// <summary>
/// Returns the real instance of the object ('this') for use in strongly typed events
/// </summary>
protected abstract TInstanceType Instance { get; }
public abstract Guid UniqueIdentifier { get; }
public abstract string Name { get; }
public virtual void RefreshAll()
{
OnCacheUpdated(Instance, new CacheRefresherEventArgs(null, MessageType.RefreshAll));
}
public virtual void Refresh(int id)
{
OnCacheUpdated(Instance, new CacheRefresherEventArgs(id, MessageType.RefreshById));
}
public virtual void Remove(int id)
{
OnCacheUpdated(Instance, new CacheRefresherEventArgs(id, MessageType.RemoveById));
}
public virtual void Refresh(Guid id)
{
OnCacheUpdated(Instance, new CacheRefresherEventArgs(id, MessageType.RefreshById));
}
/// <summary>
/// Clears the cache for all repository entities of this type
/// </summary>
/// <typeparam name="TEntity"></typeparam>
internal void ClearAllIsolatedCacheByEntityType<TEntity>()
where TEntity : class, IAggregateRoot
{
ApplicationContext.Current.ApplicationCache.IsolatedRuntimeCache.ClearCache<TEntity>();
}
}
}

View File

@@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Composing;
namespace Umbraco.Core.Cache
{
public class CacheRefresherCollection : BuilderCollectionBase<ICacheRefresher>
{
public CacheRefresherCollection(IEnumerable<ICacheRefresher> items)
: base(items)
{ }
public ICacheRefresher this[Guid id]
=> this.FirstOrDefault(x => x.RefresherUniqueId == id);
}
}

View File

@@ -1,15 +0,0 @@
using System.Collections.Generic;
using LightInject;
using Umbraco.Core.Composing;
namespace Umbraco.Core.Cache
{
public class CacheRefresherCollectionBuilder : LazyCollectionBuilderBase<CacheRefresherCollectionBuilder, CacheRefresherCollection, ICacheRefresher>
{
public CacheRefresherCollectionBuilder(IServiceContainer container)
: base(container)
{ }
protected override CacheRefresherCollectionBuilder This => this;
}
}

View File

@@ -1,19 +1,19 @@
using System;
using Umbraco.Core.Sync;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Event args for cache refresher updates
/// </summary>
public class CacheRefresherEventArgs : EventArgs
{
public CacheRefresherEventArgs(object msgObject, MessageType type)
{
MessageType = type;
MessageObject = msgObject;
}
public object MessageObject { get; private set; }
public MessageType MessageType { get; private set; }
}
}
using System;
using Umbraco.Core.Sync;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Event args for cache refresher updates
/// </summary>
public class CacheRefresherEventArgs : EventArgs
{
public CacheRefresherEventArgs(object msgObject, MessageType type)
{
MessageType = type;
MessageObject = msgObject;
}
public object MessageObject { get; private set; }
public MessageType MessageType { get; private set; }
}
}

View File

@@ -1,9 +1,9 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Caching;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Cache
{
@@ -16,26 +16,23 @@ namespace Umbraco.Core.Cache
}
/// <summary>
/// A wrapper for any IRuntimeCacheProvider that ensures that all inserts and returns
/// A wrapper for any IRuntimeCacheProvider that ensures that all inserts and returns
/// are a deep cloned copy of the item when the item is IDeepCloneable and that tracks changes are
/// reset if the object is TracksChangesEntityBase
/// </summary>
internal class DeepCloneRuntimeCacheProvider : IRuntimeCacheProvider, IRuntimeCacheProviderWrapper
{
public IRuntimeCacheProvider InnerProvider { get; }
public IRuntimeCacheProvider InnerProvider { get; private set; }
public DeepCloneRuntimeCacheProvider(IRuntimeCacheProvider innerProvider)
{
var type = typeof (DeepCloneRuntimeCacheProvider);
if (innerProvider.GetType() == type)
throw new InvalidOperationException($"A {type} cannot wrap another instance of {type}.");
if (innerProvider.GetType() == typeof(DeepCloneRuntimeCacheProvider))
throw new InvalidOperationException("A " + typeof(DeepCloneRuntimeCacheProvider) + " cannot wrap another instance of " + typeof(DeepCloneRuntimeCacheProvider));
InnerProvider = innerProvider;
}
#region Clear - doesn't require any changes
public void ClearAllCache()
{
InnerProvider.ClearAllCache();
@@ -69,8 +66,7 @@ namespace Umbraco.Core.Cache
public void ClearCacheByKeyExpression(string regexString)
{
InnerProvider.ClearCacheByKeyExpression(regexString);
}
}
#endregion
public IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
@@ -84,7 +80,7 @@ namespace Umbraco.Core.Cache
return InnerProvider.GetCacheItemsByKeyExpression(regexString)
.Select(CheckCloneableAndTracksChanges);
}
public object GetCacheItem(string cacheKey)
{
var item = InnerProvider.GetCacheItem(cacheKey);
@@ -112,11 +108,11 @@ namespace Umbraco.Core.Cache
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
if (value == null) return null; // do not store null values (backward compat)
// clone / reset to go into the cache
//Clone/reset to go into the cache
return CheckCloneableAndTracksChanges(value);
}, timeout, isSliding, priority, removedCallback, dependentFiles);
// clone / reset to go into the cache
//Clone/reset to go out of the cache
return CheckCloneableAndTracksChanges(cached);
}
@@ -128,9 +124,8 @@ namespace Umbraco.Core.Cache
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
if (value == null) return null; // do not store null values (backward compat)
// clone / reset to go into the cache
return CheckCloneableAndTracksChanges(value);
}, timeout, isSliding, priority, removedCallback, dependentFiles);
}, timeout, isSliding, priority, removedCallback, dependentFiles);
}
private static object CheckCloneableAndTracksChanges(object input)
@@ -138,10 +133,11 @@ namespace Umbraco.Core.Cache
var cloneable = input as IDeepCloneable;
if (cloneable != null)
{
input = cloneable.DeepClone();
input = cloneable.DeepClone();
}
// reset dirty initial properties (U4-1946)
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
var tracksChanges = input as IRememberBeingDirty;
if (tracksChanges != null)
{
@@ -152,4 +148,4 @@ namespace Umbraco.Core.Cache
return input;
}
}
}
}

View File

@@ -1,7 +1,7 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.Cache
@@ -18,26 +18,32 @@ namespace Umbraco.Core.Cache
/// <para>If options.GetAllCacheValidateCount then we check against the db when getting many entities.</para>
/// </remarks>
internal class DefaultRepositoryCachePolicy<TEntity, TId> : RepositoryCachePolicyBase<TEntity, TId>
where TEntity : class, IEntity
where TEntity : class, IAggregateRoot
{
private static readonly TEntity[] EmptyEntities = new TEntity[0]; // const
private readonly RepositoryCachePolicyOptions _options;
public DefaultRepositoryCachePolicy(IRuntimeCacheProvider cache, IScopeAccessor scopeAccessor, RepositoryCachePolicyOptions options)
: base(cache, scopeAccessor)
public DefaultRepositoryCachePolicy(IRuntimeCacheProvider cache, RepositoryCachePolicyOptions options)
: base(cache)
{
_options = options ?? throw new ArgumentNullException(nameof(options));
if (options == null) throw new ArgumentNullException("options");
_options = options;
}
public override IRepositoryCachePolicy<TEntity, TId> Scoped(IRuntimeCacheProvider runtimeCache, IScope scope)
{
return new ScopedRepositoryCachePolicy<TEntity, TId>(this, runtimeCache, scope);
}
protected string GetEntityCacheKey(object id)
{
if (id == null) throw new ArgumentNullException(nameof(id));
if (id == null) throw new ArgumentNullException("id");
return GetEntityTypeCacheKey() + id;
}
protected string GetEntityTypeCacheKey()
{
return $"uRepo_{typeof (TEntity).Name}_";
return string.Format("uRepo_{0}_", typeof(TEntity).Name);
}
protected virtual void InsertEntity(string cacheKey, TEntity entity)
@@ -68,7 +74,7 @@ namespace Umbraco.Core.Cache
/// <inheritdoc />
public override void Create(TEntity entity, Action<TEntity> persistNew)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
if (entity == null) throw new ArgumentNullException("entity");
try
{
@@ -100,7 +106,7 @@ namespace Umbraco.Core.Cache
/// <inheritdoc />
public override void Update(TEntity entity, Action<TEntity> persistUpdated)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
if (entity == null) throw new ArgumentNullException("entity");
try
{
@@ -132,7 +138,7 @@ namespace Umbraco.Core.Cache
/// <inheritdoc />
public override void Delete(TEntity entity, Action<TEntity> persistDeleted)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
if (entity == null) throw new ArgumentNullException("entity");
try
{
@@ -238,7 +244,7 @@ namespace Umbraco.Core.Cache
/// <inheritdoc />
public override void ClearAll()
{
Cache.ClearCacheByKeySearch(GetEntityTypeCacheKey());
Cache.ClearAllCache();
}
}
}
}

View File

@@ -1,147 +0,0 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Umbraco.Core.Composing;
namespace Umbraco.Core.Cache
{
internal class DictionaryCacheProvider : ICacheProvider
{
private readonly ConcurrentDictionary<string, Lazy<object>> _items
= new ConcurrentDictionary<string, Lazy<object>>();
// for tests
internal ConcurrentDictionary<string, Lazy<object>> Items => _items;
public void ClearAllCache()
{
_items.Clear();
}
public void ClearCacheItem(string key)
{
_items.TryRemove(key, out _);
}
public void ClearCacheObjectTypes(string typeName)
{
var type = TypeFinder.GetTypeByName(typeName);
if (type == null) return;
var isInterface = type.IsInterface;
foreach (var kvp in _items
.Where(x =>
{
// entry.Value is Lazy<object> and not null, its value may be null
// remove null values as well, does not hurt
// get non-created as NonCreatedValue & exceptions as null
var value = DictionaryCacheProviderBase.GetSafeLazyValue(x.Value, true);
// if T is an interface remove anything that implements that interface
// otherwise remove exact types (not inherited types)
return value == null || (isInterface ? (type.IsInstanceOfType(value)) : (value.GetType() == type));
}))
_items.TryRemove(kvp.Key, out _);
}
public void ClearCacheObjectTypes<T>()
{
var typeOfT = typeof(T);
var isInterface = typeOfT.IsInterface;
foreach (var kvp in _items
.Where(x =>
{
// entry.Value is Lazy<object> and not null, its value may be null
// remove null values as well, does not hurt
// compare on exact type, don't use "is"
// get non-created as NonCreatedValue & exceptions as null
var value = DictionaryCacheProviderBase.GetSafeLazyValue(x.Value, true);
// if T is an interface remove anything that implements that interface
// otherwise remove exact types (not inherited types)
return value == null || (isInterface ? (value is T) : (value.GetType() == typeOfT));
}))
_items.TryRemove(kvp.Key, out _);
}
public void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
{
var typeOfT = typeof(T);
var isInterface = typeOfT.IsInterface;
foreach (var kvp in _items
.Where(x =>
{
// entry.Value is Lazy<object> and not null, its value may be null
// remove null values as well, does not hurt
// compare on exact type, don't use "is"
// get non-created as NonCreatedValue & exceptions as null
var value = DictionaryCacheProviderBase.GetSafeLazyValue(x.Value, true);
if (value == null) return true;
// if T is an interface remove anything that implements that interface
// otherwise remove exact types (not inherited types)
return (isInterface ? (value is T) : (value.GetType() == typeOfT))
// run predicate on the 'public key' part only, ie without prefix
&& predicate(x.Key, (T)value);
}))
_items.TryRemove(kvp.Key, out _);
}
public void ClearCacheByKeySearch(string keyStartsWith)
{
foreach (var ikvp in _items
.Where(kvp => kvp.Key.InvariantStartsWith(keyStartsWith)))
_items.TryRemove(ikvp.Key, out _);
}
public void ClearCacheByKeyExpression(string regexString)
{
foreach (var ikvp in _items
.Where(kvp => Regex.IsMatch(kvp.Key, regexString)))
_items.TryRemove(ikvp.Key, out _);
}
public IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
{
return _items
.Where(kvp => kvp.Key.InvariantStartsWith(keyStartsWith))
.Select(kvp => DictionaryCacheProviderBase.GetSafeLazyValue(kvp.Value))
.Where(x => x != null);
}
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
{
return _items
.Where(kvp => Regex.IsMatch(kvp.Key, regexString))
.Select(kvp => DictionaryCacheProviderBase.GetSafeLazyValue(kvp.Value))
.Where(x => x != null);
}
public object GetCacheItem(string cacheKey)
{
_items.TryGetValue(cacheKey, out var result); // else null
return result == null ? null : DictionaryCacheProviderBase.GetSafeLazyValue(result); // return exceptions as null
}
public object GetCacheItem(string cacheKey, Func<object> getCacheItem)
{
var result = _items.GetOrAdd(cacheKey, k => DictionaryCacheProviderBase.GetSafeLazy(getCacheItem));
var value = result.Value; // will not throw (safe lazy)
if (!(value is DictionaryCacheProviderBase.ExceptionHolder eh))
return value;
// and... it's in the cache anyway - so contrary to other cache providers,
// which would trick with GetSafeLazyValue, we need to remove by ourselves,
// in order NOT to cache exceptions
_items.TryRemove(cacheKey, out result);
eh.Exception.Throw(); // throw once!
return null; // never reached
}
}
}

View File

@@ -1,313 +1,255 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Text.RegularExpressions;
using Umbraco.Core.Composing;
namespace Umbraco.Core.Cache
{
internal abstract class DictionaryCacheProviderBase : ICacheProvider
{
// prefix cache keys so we know which one are ours
protected const string CacheItemPrefix = "umbrtmche";
// an object that represent a value that has not been created yet
protected internal static readonly object ValueNotCreated = new object();
// manupulate the underlying cache entries
// these *must* be called from within the appropriate locks
// and use the full prefixed cache keys
protected abstract IEnumerable<DictionaryEntry> GetDictionaryEntries();
protected abstract void RemoveEntry(string key);
protected abstract object GetEntry(string key);
// read-write lock the underlying cache
//protected abstract IDisposable ReadLock { get; }
//protected abstract IDisposable WriteLock { get; }
protected abstract void EnterReadLock();
protected abstract void ExitReadLock();
protected abstract void EnterWriteLock();
protected abstract void ExitWriteLock();
protected string GetCacheKey(string key)
{
return string.Format("{0}-{1}", CacheItemPrefix, key);
}
protected internal static Lazy<object> GetSafeLazy(Func<object> getCacheItem)
{
// try to generate the value and if it fails,
// wrap in an ExceptionHolder - would be much simpler
// to just use lazy.IsValueFaulted alas that field is
// internal
return new Lazy<object>(() =>
{
try
{
return getCacheItem();
}
catch (Exception e)
{
return new ExceptionHolder(ExceptionDispatchInfo.Capture(e));
}
});
}
protected internal static object GetSafeLazyValue(Lazy<object> lazy, bool onlyIfValueIsCreated = false)
{
// if onlyIfValueIsCreated, do not trigger value creation
// must return something, though, to differenciate from null values
if (onlyIfValueIsCreated && lazy.IsValueCreated == false) return ValueNotCreated;
// if execution has thrown then lazy.IsValueCreated is false
// and lazy.IsValueFaulted is true (but internal) so we use our
// own exception holder (see Lazy<T> source code) to return null
if (lazy.Value is ExceptionHolder) return null;
// we have a value and execution has not thrown so returning
// here does not throw - unless we're re-entering, take care of it
try
{
return lazy.Value;
}
catch (InvalidOperationException e)
{
throw new InvalidOperationException("The method that computes a value for the cache has tried to read that value from the cache.", e);
}
}
internal class ExceptionHolder
{
public ExceptionHolder(ExceptionDispatchInfo e)
{
Exception = e;
}
public ExceptionDispatchInfo Exception { get; }
}
#region Clear
public virtual void ClearAllCache()
{
try
{
EnterWriteLock();
foreach (var entry in GetDictionaryEntries()
.ToArray())
RemoveEntry((string) entry.Key);
}
finally
{
ExitWriteLock();
}
}
public virtual void ClearCacheItem(string key)
{
var cacheKey = GetCacheKey(key);
try
{
EnterWriteLock();
RemoveEntry(cacheKey);
}
finally
{
ExitWriteLock();
}
}
public virtual void ClearCacheObjectTypes(string typeName)
{
var type = TypeFinder.GetTypeByName(typeName);
if (type == null) return;
var isInterface = type.IsInterface;
try
{
EnterWriteLock();
foreach (var entry in GetDictionaryEntries()
.Where(x =>
{
// entry.Value is Lazy<object> and not null, its value may be null
// remove null values as well, does not hurt
// get non-created as NonCreatedValue & exceptions as null
var value = GetSafeLazyValue((Lazy<object>) x.Value, true);
// if T is an interface remove anything that implements that interface
// otherwise remove exact types (not inherited types)
return value == null || (isInterface ? (type.IsInstanceOfType(value)) : (value.GetType() == type));
})
.ToArray())
RemoveEntry((string) entry.Key);
}
finally
{
ExitWriteLock();
}
}
public virtual void ClearCacheObjectTypes<T>()
{
var typeOfT = typeof(T);
var isInterface = typeOfT.IsInterface;
try
{
EnterWriteLock();
foreach (var entry in GetDictionaryEntries()
.Where(x =>
{
// entry.Value is Lazy<object> and not null, its value may be null
// remove null values as well, does not hurt
// compare on exact type, don't use "is"
// get non-created as NonCreatedValue & exceptions as null
var value = GetSafeLazyValue((Lazy<object>) x.Value, true);
// if T is an interface remove anything that implements that interface
// otherwise remove exact types (not inherited types)
return value == null || (isInterface ? (value is T) : (value.GetType() == typeOfT));
})
.ToArray())
RemoveEntry((string) entry.Key);
}
finally
{
ExitWriteLock();
}
}
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
{
var typeOfT = typeof(T);
var isInterface = typeOfT.IsInterface;
var plen = CacheItemPrefix.Length + 1;
try
{
EnterWriteLock();
foreach (var entry in GetDictionaryEntries()
.Where(x =>
{
// entry.Value is Lazy<object> and not null, its value may be null
// remove null values as well, does not hurt
// compare on exact type, don't use "is"
// get non-created as NonCreatedValue & exceptions as null
var value = GetSafeLazyValue((Lazy<object>) x.Value, true);
if (value == null) return true;
// if T is an interface remove anything that implements that interface
// otherwise remove exact types (not inherited types)
return (isInterface ? (value is T) : (value.GetType() == typeOfT))
// run predicate on the 'public key' part only, ie without prefix
&& predicate(((string) x.Key).Substring(plen), (T) value);
}))
RemoveEntry((string) entry.Key);
}
finally
{
ExitWriteLock();
}
}
public virtual void ClearCacheByKeySearch(string keyStartsWith)
{
var plen = CacheItemPrefix.Length + 1;
try
{
EnterWriteLock();
foreach (var entry in GetDictionaryEntries()
.Where(x => ((string)x.Key).Substring(plen).InvariantStartsWith(keyStartsWith))
.ToArray())
RemoveEntry((string) entry.Key);
}
finally
{
ExitWriteLock();
}
}
public virtual void ClearCacheByKeyExpression(string regexString)
{
var plen = CacheItemPrefix.Length + 1;
try
{
EnterWriteLock();
foreach (var entry in GetDictionaryEntries()
.Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString))
.ToArray())
RemoveEntry((string) entry.Key);
}
finally
{
ExitWriteLock();
}
}
#endregion
#region Get
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
{
var plen = CacheItemPrefix.Length + 1;
IEnumerable<DictionaryEntry> entries;
try
{
EnterReadLock();
entries = GetDictionaryEntries()
.Where(x => ((string)x.Key).Substring(plen).InvariantStartsWith(keyStartsWith))
.ToArray(); // evaluate while locked
}
finally
{
ExitReadLock();
}
return entries
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
.Where(x => x != null); // backward compat, don't store null values in the cache
}
public virtual IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
{
const string prefix = CacheItemPrefix + "-";
var plen = prefix.Length;
IEnumerable<DictionaryEntry> entries;
try
{
EnterReadLock();
entries = GetDictionaryEntries()
.Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString))
.ToArray(); // evaluate while locked
}
finally
{
ExitReadLock();
}
return entries
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
.Where(x => x != null); // backward compat, don't store null values in the cache
}
public virtual object GetCacheItem(string cacheKey)
{
cacheKey = GetCacheKey(cacheKey);
Lazy<object> result;
try
{
EnterReadLock();
result = GetEntry(cacheKey) as Lazy<object>; // null if key not found
}
finally
{
ExitReadLock();
}
return result == null ? null : GetSafeLazyValue(result); // return exceptions as null
}
public abstract object GetCacheItem(string cacheKey, Func<object> getCacheItem);
#endregion
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace Umbraco.Core.Cache
{
internal abstract class DictionaryCacheProviderBase : ICacheProvider
{
// prefix cache keys so we know which one are ours
protected const string CacheItemPrefix = "umbrtmche";
// an object that represent a value that has not been created yet
protected internal static readonly object ValueNotCreated = new object();
// manupulate the underlying cache entries
// these *must* be called from within the appropriate locks
// and use the full prefixed cache keys
protected abstract IEnumerable<DictionaryEntry> GetDictionaryEntries();
protected abstract void RemoveEntry(string key);
protected abstract object GetEntry(string key);
// read-write lock the underlying cache
protected abstract IDisposable ReadLock { get; }
protected abstract IDisposable WriteLock { get; }
protected string GetCacheKey(string key)
{
return string.Format("{0}-{1}", CacheItemPrefix, key);
}
protected internal static Lazy<object> GetSafeLazy(Func<object> getCacheItem)
{
// try to generate the value and if it fails,
// wrap in an ExceptionHolder - would be much simpler
// to just use lazy.IsValueFaulted alas that field is
// internal
return new Lazy<object>(() =>
{
try
{
return getCacheItem();
}
catch (Exception e)
{
return new ExceptionHolder(e);
}
});
}
protected internal static object GetSafeLazyValue(Lazy<object> lazy, bool onlyIfValueIsCreated = false)
{
// if onlyIfValueIsCreated, do not trigger value creation
// must return something, though, to differenciate from null values
if (onlyIfValueIsCreated && lazy.IsValueCreated == false) return ValueNotCreated;
// if execution has thrown then lazy.IsValueCreated is false
// and lazy.IsValueFaulted is true (but internal) so we use our
// own exception holder (see Lazy<T> source code) to return null
if (lazy.Value is ExceptionHolder) return null;
// we have a value and execution has not thrown so returning
// here does not throw - unless we're re-entering, take care of it
try
{
return lazy.Value;
}
catch (InvalidOperationException e)
{
throw new InvalidOperationException("The method that computes a value for the cache has tried to read that value from the cache.", e);
}
}
internal class ExceptionHolder
{
public ExceptionHolder(Exception e)
{
Exception = e;
}
public Exception Exception { get; private set; }
}
#region Clear
public virtual void ClearAllCache()
{
using (WriteLock)
{
foreach (var entry in GetDictionaryEntries()
.ToArray())
RemoveEntry((string) entry.Key);
}
}
public virtual void ClearCacheItem(string key)
{
var cacheKey = GetCacheKey(key);
using (WriteLock)
{
RemoveEntry(cacheKey);
}
}
public virtual void ClearCacheObjectTypes(string typeName)
{
var type = TypeFinder.GetTypeByName(typeName);
if (type == null) return;
var isInterface = type.IsInterface;
using (WriteLock)
{
foreach (var entry in GetDictionaryEntries()
.Where(x =>
{
// entry.Value is Lazy<object> and not null, its value may be null
// remove null values as well, does not hurt
// get non-created as NonCreatedValue & exceptions as null
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
// if T is an interface remove anything that implements that interface
// otherwise remove exact types (not inherited types)
return value == null || (isInterface ? (type.IsInstanceOfType(value)) : (value.GetType() == type));
})
.ToArray())
RemoveEntry((string) entry.Key);
}
}
public virtual void ClearCacheObjectTypes<T>()
{
var typeOfT = typeof(T);
var isInterface = typeOfT.IsInterface;
using (WriteLock)
{
foreach (var entry in GetDictionaryEntries()
.Where(x =>
{
// entry.Value is Lazy<object> and not null, its value may be null
// remove null values as well, does not hurt
// compare on exact type, don't use "is"
// get non-created as NonCreatedValue & exceptions as null
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
// if T is an interface remove anything that implements that interface
// otherwise remove exact types (not inherited types)
return value == null || (isInterface ? (value is T) : (value.GetType() == typeOfT));
})
.ToArray())
RemoveEntry((string) entry.Key);
}
}
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
{
var typeOfT = typeof(T);
var isInterface = typeOfT.IsInterface;
var plen = CacheItemPrefix.Length + 1;
using (WriteLock)
{
foreach (var entry in GetDictionaryEntries()
.Where(x =>
{
// entry.Value is Lazy<object> and not null, its value may be null
// remove null values as well, does not hurt
// compare on exact type, don't use "is"
// get non-created as NonCreatedValue & exceptions as null
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
if (value == null) return true;
// if T is an interface remove anything that implements that interface
// otherwise remove exact types (not inherited types)
return (isInterface ? (value is T) : (value.GetType() == typeOfT))
// run predicate on the 'public key' part only, ie without prefix
&& predicate(((string) x.Key).Substring(plen), (T) value);
}))
RemoveEntry((string) entry.Key);
}
}
public virtual void ClearCacheByKeySearch(string keyStartsWith)
{
var plen = CacheItemPrefix.Length + 1;
using (WriteLock)
{
foreach (var entry in GetDictionaryEntries()
.Where(x => ((string)x.Key).Substring(plen).InvariantStartsWith(keyStartsWith))
.ToArray())
RemoveEntry((string) entry.Key);
}
}
public virtual void ClearCacheByKeyExpression(string regexString)
{
var plen = CacheItemPrefix.Length + 1;
using (WriteLock)
{
foreach (var entry in GetDictionaryEntries()
.Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString))
.ToArray())
RemoveEntry((string) entry.Key);
}
}
#endregion
#region Get
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
{
var plen = CacheItemPrefix.Length + 1;
IEnumerable<DictionaryEntry> entries;
using (ReadLock)
{
entries = GetDictionaryEntries()
.Where(x => ((string)x.Key).Substring(plen).InvariantStartsWith(keyStartsWith))
.ToArray(); // evaluate while locked
}
return entries
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
.Where(x => x != null); // backward compat, don't store null values in the cache
}
public virtual IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
{
const string prefix = CacheItemPrefix + "-";
var plen = prefix.Length;
IEnumerable<DictionaryEntry> entries;
using (ReadLock)
{
entries = GetDictionaryEntries()
.Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString))
.ToArray(); // evaluate while locked
}
return entries
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
.Where(x => x != null); // backward compat, don't store null values in the cache
}
public virtual object GetCacheItem(string cacheKey)
{
cacheKey = GetCacheKey(cacheKey);
Lazy<object> result;
using (ReadLock)
{
result = GetEntry(cacheKey) as Lazy<object>; // null if key not found
}
return result == null ? null : GetSafeLazyValue(result); // return exceptions as null
}
public abstract object GetCacheItem(string cacheKey, Func<object> getCacheItem);
#endregion
}
}

View File

@@ -1,8 +1,8 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Collections;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.Cache
@@ -19,23 +19,28 @@ namespace Umbraco.Core.Cache
/// keep as a whole in memory.</para>
/// </remarks>
internal class FullDataSetRepositoryCachePolicy<TEntity, TId> : RepositoryCachePolicyBase<TEntity, TId>
where TEntity : class, IEntity
where TEntity : class, IAggregateRoot
{
private readonly Func<TEntity, TId> _entityGetId;
private readonly bool _expires;
public FullDataSetRepositoryCachePolicy(IRuntimeCacheProvider cache, IScopeAccessor scopeAccessor, Func<TEntity, TId> entityGetId, bool expires)
: base(cache, scopeAccessor)
public FullDataSetRepositoryCachePolicy(IRuntimeCacheProvider cache, Func<TEntity, TId> entityGetId, bool expires)
: base(cache)
{
_entityGetId = entityGetId;
_expires = expires;
}
public override IRepositoryCachePolicy<TEntity, TId> Scoped(IRuntimeCacheProvider runtimeCache, IScope scope)
{
return new ScopedRepositoryCachePolicy<TEntity, TId>(this, runtimeCache, scope);
}
protected static readonly TId[] EmptyIds = new TId[0]; // const
protected string GetEntityTypeCacheKey()
{
return $"uRepo_{typeof (TEntity).Name}_";
return string.Format("uRepo_{0}_", typeof(TEntity).Name);
}
protected void InsertEntities(TEntity[] entities)
@@ -51,22 +56,20 @@ namespace Umbraco.Core.Cache
// set to ListCloneBehavior.CloneOnce ie it will clone *once* when inserting,
// and then will *not* clone when retrieving.
var key = GetEntityTypeCacheKey();
if (_expires)
{
Cache.InsertCacheItem(key, () => new DeepCloneableList<TEntity>(entities), TimeSpan.FromMinutes(5), true);
Cache.InsertCacheItem(GetEntityTypeCacheKey(), () => new DeepCloneableList<TEntity>(entities), TimeSpan.FromMinutes(5), true);
}
else
{
Cache.InsertCacheItem(key, () => new DeepCloneableList<TEntity>(entities));
Cache.InsertCacheItem(GetEntityTypeCacheKey(), () => new DeepCloneableList<TEntity>(entities));
}
}
/// <inheritdoc />
public override void Create(TEntity entity, Action<TEntity> persistNew)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
if (entity == null) throw new ArgumentNullException("entity");
try
{
@@ -81,7 +84,7 @@ namespace Umbraco.Core.Cache
/// <inheritdoc />
public override void Update(TEntity entity, Action<TEntity> persistUpdated)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
if (entity == null) throw new ArgumentNullException("entity");
try
{
@@ -96,7 +99,7 @@ namespace Umbraco.Core.Cache
/// <inheritdoc />
public override void Delete(TEntity entity, Action<TEntity> persistDeleted)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
if (entity == null) throw new ArgumentNullException("entity");
try
{
@@ -117,7 +120,7 @@ namespace Umbraco.Core.Cache
// see note in InsertEntities - what we get here is the original
// cached entity, not a clone, so we need to manually ensure it is deep-cloned.
return (TEntity)entity?.DeepClone();
return entity == null ? null : (TEntity) entity.DeepClone();
}
/// <inheritdoc />
@@ -125,11 +128,11 @@ namespace Umbraco.Core.Cache
{
// get all from the cache -- and only the cache, then look for the entity
var all = Cache.GetCacheItem<DeepCloneableList<TEntity>>(GetEntityTypeCacheKey());
var entity = all?.FirstOrDefault(x => _entityGetId(x).Equals(id));
var entity = all == null ? null : all.FirstOrDefault(x => _entityGetId(x).Equals(id));
// see note in InsertEntities - what we get here is the original
// cached entity, not a clone, so we need to manually ensure it is deep-cloned.
return (TEntity) entity?.DeepClone();
return entity == null ? null : (TEntity)entity.DeepClone();
}
/// <inheritdoc />
@@ -156,7 +159,7 @@ namespace Umbraco.Core.Cache
}
// does NOT clone anything, so be nice with the returned values
internal IEnumerable<TEntity> GetAllCached(Func<TId[], IEnumerable<TEntity>> performGetAll)
private IEnumerable<TEntity> GetAllCached(Func<TId[], IEnumerable<TEntity>> performGetAll)
{
// try the cache first
var all = Cache.GetCacheItem<DeepCloneableList<TEntity>>(GetEntityTypeCacheKey());
@@ -174,4 +177,4 @@ namespace Umbraco.Core.Cache
Cache.ClearCacheItem(GetEntityTypeCacheKey());
}
}
}
}

View File

@@ -1,161 +1,155 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Umbraco.Core.Cache
{
/// <summary>
/// A cache provider that caches items in the HttpContext.Items
/// </summary>
/// <remarks>
/// If the Items collection is null, then this provider has no effect
/// </remarks>
internal class HttpRequestCacheProvider : DictionaryCacheProviderBase
{
// context provider
// the idea is that there is only one, application-wide HttpRequestCacheProvider instance,
// that is initialized with a method that returns the "current" context.
// NOTE
// but then it is initialized with () => new HttpContextWrapper(HttpContent.Current)
// which is higly inefficient because it creates a new wrapper each time we refer to _context()
// so replace it with _context1 and _context2 below + a way to get context.Items.
//private readonly Func<HttpContextBase> _context;
// NOTE
// and then in almost 100% cases _context2 will be () => HttpContext.Current
// so why not bring that logic in here and fallback on to HttpContext.Current when
// _context1 is null?
//private readonly HttpContextBase _context1;
//private readonly Func<HttpContext> _context2;
private readonly HttpContextBase _context;
private IDictionary ContextItems
{
//get { return _context1 != null ? _context1.Items : _context2().Items; }
get { return _context != null ? _context.Items : HttpContext.Current.Items; }
}
private bool HasContextItems
{
get { return (_context != null && _context.Items != null) || HttpContext.Current != null; }
}
// for unit tests
public HttpRequestCacheProvider(HttpContextBase context)
{
_context = context;
}
// main constructor
// will use HttpContext.Current
public HttpRequestCacheProvider(/*Func<HttpContext> context*/)
{
//_context2 = context;
}
protected override IEnumerable<DictionaryEntry> GetDictionaryEntries()
{
const string prefix = CacheItemPrefix + "-";
if (HasContextItems == false) return Enumerable.Empty<DictionaryEntry>();
return ContextItems.Cast<DictionaryEntry>()
.Where(x => x.Key is string && ((string)x.Key).StartsWith(prefix));
}
protected override void RemoveEntry(string key)
{
if (HasContextItems == false) return;
ContextItems.Remove(key);
}
protected override object GetEntry(string key)
{
return HasContextItems ? ContextItems[key] : null;
}
#region Lock
private bool _entered;
protected override void EnterReadLock() => EnterWriteLock();
protected override void EnterWriteLock()
{
if (HasContextItems)
{
System.Threading.Monitor.Enter(ContextItems.SyncRoot, ref _entered);
}
}
protected override void ExitReadLock() => ExitWriteLock();
protected override void ExitWriteLock()
{
if (_entered)
{
_entered = false;
System.Threading.Monitor.Exit(ContextItems.SyncRoot);
}
}
#endregion
#region Get
public override object GetCacheItem(string cacheKey, Func<object> getCacheItem)
{
//no place to cache so just return the callback result
if (HasContextItems == false) return getCacheItem();
cacheKey = GetCacheKey(cacheKey);
Lazy<object> result;
try
{
EnterWriteLock();
result = ContextItems[cacheKey] as Lazy<object>; // null if key not found
// cannot create value within the lock, so if result.IsValueCreated is false, just
// do nothing here - means that if creation throws, a race condition could cause
// more than one thread to reach the return statement below and throw - accepted.
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
{
result = GetSafeLazy(getCacheItem);
ContextItems[cacheKey] = result;
}
}
finally
{
ExitWriteLock();
}
// using GetSafeLazy and GetSafeLazyValue ensures that we don't cache
// exceptions (but try again and again) and silently eat them - however at
// some point we have to report them - so need to re-throw here
// this does not throw anymore
//return result.Value;
var value = result.Value; // will not throw (safe lazy)
if (value is ExceptionHolder eh) eh.Exception.Throw(); // throw once!
return value;
}
#endregion
#region Insert
#endregion
private class NoopLocker : DisposableObjectSlim
{
protected override void DisposeResources()
{ }
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Umbraco.Core.Cache
{
/// <summary>
/// A cache provider that caches items in the HttpContext.Items
/// </summary>
/// <remarks>
/// If the Items collection is null, then this provider has no effect
/// </remarks>
internal class HttpRequestCacheProvider : DictionaryCacheProviderBase
{
// context provider
// the idea is that there is only one, application-wide HttpRequestCacheProvider instance,
// that is initialized with a method that returns the "current" context.
// NOTE
// but then it is initialized with () => new HttpContextWrapper(HttpContent.Current)
// which is higly inefficient because it creates a new wrapper each time we refer to _context()
// so replace it with _context1 and _context2 below + a way to get context.Items.
//private readonly Func<HttpContextBase> _context;
// NOTE
// and then in almost 100% cases _context2 will be () => HttpContext.Current
// so why not bring that logic in here and fallback on to HttpContext.Current when
// _context1 is null?
//private readonly HttpContextBase _context1;
//private readonly Func<HttpContext> _context2;
private readonly HttpContextBase _context;
private IDictionary ContextItems
{
//get { return _context1 != null ? _context1.Items : _context2().Items; }
get { return _context != null ? _context.Items : HttpContext.Current.Items; }
}
private bool HasContextItems
{
get { return (_context != null && _context.Items != null) || HttpContext.Current != null; }
}
// for unit tests
public HttpRequestCacheProvider(HttpContextBase context)
{
_context = context;
}
// main constructor
// will use HttpContext.Current
public HttpRequestCacheProvider(/*Func<HttpContext> context*/)
{
//_context2 = context;
}
protected override IEnumerable<DictionaryEntry> GetDictionaryEntries()
{
const string prefix = CacheItemPrefix + "-";
if (HasContextItems == false) return Enumerable.Empty<DictionaryEntry>();
return ContextItems.Cast<DictionaryEntry>()
.Where(x => x.Key is string && ((string)x.Key).StartsWith(prefix));
}
protected override void RemoveEntry(string key)
{
if (HasContextItems == false) return;
ContextItems.Remove(key);
}
protected override object GetEntry(string key)
{
return HasContextItems ? ContextItems[key] : null;
}
#region Lock
protected override IDisposable ReadLock
{
// there's no difference between ReadLock and WriteLock here
get { return WriteLock; }
}
protected override IDisposable WriteLock
{
// NOTE
// could think about just overriding base.Locker to return a different
// object but then we'd create a ReaderWriterLockSlim per request,
// which is less efficient than just using a basic monitor lock.
get
{
return HasContextItems
? (IDisposable) new MonitorLock(ContextItems.SyncRoot)
: new NoopLocker();
}
}
#endregion
#region Get
public override object GetCacheItem(string cacheKey, Func<object> getCacheItem)
{
//no place to cache so just return the callback result
if (HasContextItems == false) return getCacheItem();
cacheKey = GetCacheKey(cacheKey);
Lazy<object> result;
using (WriteLock)
{
result = ContextItems[cacheKey] as Lazy<object>; // null if key not found
// cannot create value within the lock, so if result.IsValueCreated is false, just
// do nothing here - means that if creation throws, a race condition could cause
// more than one thread to reach the return statement below and throw - accepted.
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
{
result = GetSafeLazy(getCacheItem);
ContextItems[cacheKey] = result;
}
}
// using GetSafeLazy and GetSafeLazyValue ensures that we don't cache
// exceptions (but try again and again) and silently eat them - however at
// some point we have to report them - so need to re-throw here
// this does not throw anymore
//return result.Value;
var value = result.Value; // will not throw (safe lazy)
var eh = value as ExceptionHolder;
if (eh != null) throw eh.Exception; // throw once!
return value;
}
#endregion
#region Insert
#endregion
private class NoopLocker : DisposableObjectSlim
{
protected override void DisposeResources()
{ }
}
}
}

View File

@@ -1,241 +1,218 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web.Caching;
using CacheItemPriority = System.Web.Caching.CacheItemPriority;
namespace Umbraco.Core.Cache
{
/// <summary>
/// A CacheProvider that wraps the logic of the HttpRuntime.Cache
/// </summary>
internal class HttpRuntimeCacheProvider : DictionaryCacheProviderBase, IRuntimeCacheProvider
{
// locker object that supports upgradeable read locking
// does not need to support recursion if we implement the cache correctly and ensure
// that methods cannot be reentrant, ie we do NOT create values while holding a lock.
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
private readonly System.Web.Caching.Cache _cache;
/// <summary>
/// Used for debugging
/// </summary>
internal Guid InstanceId { get; private set; }
public HttpRuntimeCacheProvider(System.Web.Caching.Cache cache)
{
_cache = cache;
InstanceId = Guid.NewGuid();
}
protected override IEnumerable<DictionaryEntry> GetDictionaryEntries()
{
const string prefix = CacheItemPrefix + "-";
return _cache.Cast<DictionaryEntry>()
.Where(x => x.Key is string && ((string)x.Key).StartsWith(prefix));
}
protected override void RemoveEntry(string key)
{
_cache.Remove(key);
}
protected override object GetEntry(string key)
{
return _cache.Get(key);
}
#region Lock
protected override void EnterReadLock()
{
_locker.EnterReadLock();
}
protected override void EnterWriteLock()
{
_locker.EnterWriteLock();;
}
protected override void ExitReadLock()
{
if (_locker.IsReadLockHeld)
_locker.ExitReadLock();
}
protected override void ExitWriteLock()
{
if (_locker.IsWriteLockHeld)
_locker.ExitWriteLock();
}
#endregion
#region Get
/// <summary>
/// Gets (and adds if necessary) an item from the cache with all of the default parameters
/// </summary>
/// <param name="cacheKey"></param>
/// <param name="getCacheItem"></param>
/// <returns></returns>
public override object GetCacheItem(string cacheKey, Func<object> getCacheItem)
{
return GetCacheItem(cacheKey, getCacheItem, null, dependentFiles: null);
}
/// <summary>
/// This overload is here for legacy purposes
/// </summary>
/// <param name="cacheKey"></param>
/// <param name="getCacheItem"></param>
/// <param name="timeout"></param>
/// <param name="isSliding"></param>
/// <param name="priority"></param>
/// <param name="removedCallback"></param>
/// <param name="dependency"></param>
/// <returns></returns>
internal object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null)
{
cacheKey = GetCacheKey(cacheKey);
// NOTE - because we don't know what getCacheItem does, how long it will take and whether it will hang,
// getCacheItem should run OUTSIDE of the global application lock else we run into lock contention and
// nasty performance issues.
// So.... we insert a Lazy<object> in the cache while holding the global application lock, and then rely
// on the Lazy lock to ensure that getCacheItem runs once and everybody waits on it, while the global
// application lock has been released.
// NOTE
// The Lazy value creation may produce a null value.
// Must make sure (for backward compatibility) that we pretend they are not in the cache.
// So if we find an entry in the cache that already has its value created and is null,
// pretend it was not there. If value is not already created, wait... and return null, that's
// what prior code did.
// NOTE
// The Lazy value creation may throw.
// So... the null value _will_ be in the cache but never returned
Lazy<object> result;
// Fast!
// Only one thread can enter an UpgradeableReadLock at a time, but it does not prevent other
// threads to enter a ReadLock in the meantime -- only upgrading to WriteLock will prevent all
// reads. We first try with a normal ReadLock for maximum concurrency and take the penalty of
// having to re-lock in case there's no value. Would need to benchmark to figure out whether
// it's worth it, though...
try
{
_locker.EnterReadLock();
result = _cache.Get(cacheKey) as Lazy<object>; // null if key not found
}
finally
{
if (_locker.IsReadLockHeld)
_locker.ExitReadLock();
}
var value = result == null ? null : GetSafeLazyValue(result);
if (value != null) return value;
using (var lck = new UpgradeableReadLock(_locker))
{
result = _cache.Get(cacheKey) as Lazy<object>; // null if key not found
// cannot create value within the lock, so if result.IsValueCreated is false, just
// do nothing here - means that if creation throws, a race condition could cause
// more than one thread to reach the return statement below and throw - accepted.
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
{
result = GetSafeLazy(getCacheItem);
var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration);
lck.UpgradeToWriteLock();
//NOTE: 'Insert' on System.Web.Caching.Cache actually does an add or update!
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
}
}
// using GetSafeLazy and GetSafeLazyValue ensures that we don't cache
// exceptions (but try again and again) and silently eat them - however at
// some point we have to report them - so need to re-throw here
// this does not throw anymore
//return result.Value;
value = result.Value; // will not throw (safe lazy)
if (value is ExceptionHolder eh) eh.Exception.Throw(); // throw once!
return value;
}
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
CacheDependency dependency = null;
if (dependentFiles != null && dependentFiles.Any())
{
dependency = new CacheDependency(dependentFiles);
}
return GetCacheItem(cacheKey, getCacheItem, timeout, isSliding, priority, removedCallback, dependency);
}
#endregion
#region Insert
/// <summary>
/// This overload is here for legacy purposes
/// </summary>
/// <param name="cacheKey"></param>
/// <param name="getCacheItem"></param>
/// <param name="timeout"></param>
/// <param name="isSliding"></param>
/// <param name="priority"></param>
/// <param name="removedCallback"></param>
/// <param name="dependency"></param>
internal void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null)
{
// NOTE - here also we must insert a Lazy<object> but we can evaluate it right now
// and make sure we don't store a null value.
var result = GetSafeLazy(getCacheItem);
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
if (value == null) return; // do not store null values (backward compat)
cacheKey = GetCacheKey(cacheKey);
var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration);
try
{
_locker.EnterWriteLock();
//NOTE: 'Insert' on System.Web.Caching.Cache actually does an add or update!
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
}
finally
{
if (_locker.IsWriteLockHeld)
_locker.ExitWriteLock();
}
}
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
CacheDependency dependency = null;
if (dependentFiles != null && dependentFiles.Any())
{
dependency = new CacheDependency(dependentFiles);
}
InsertCacheItem(cacheKey, getCacheItem, timeout, isSliding, priority, removedCallback, dependency);
}
#endregion
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web.Caching;
using CacheItemPriority = System.Web.Caching.CacheItemPriority;
namespace Umbraco.Core.Cache
{
/// <summary>
/// A CacheProvider that wraps the logic of the HttpRuntime.Cache
/// </summary>
internal class HttpRuntimeCacheProvider : DictionaryCacheProviderBase, IRuntimeCacheProvider
{
// locker object that supports upgradeable read locking
// does not need to support recursion if we implement the cache correctly and ensure
// that methods cannot be reentrant, ie we do NOT create values while holding a lock.
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
private readonly System.Web.Caching.Cache _cache;
/// <summary>
/// Used for debugging
/// </summary>
internal Guid InstanceId { get; private set; }
public HttpRuntimeCacheProvider(System.Web.Caching.Cache cache)
{
_cache = cache;
InstanceId = Guid.NewGuid();
}
protected override IEnumerable<DictionaryEntry> GetDictionaryEntries()
{
const string prefix = CacheItemPrefix + "-";
return _cache.Cast<DictionaryEntry>()
.Where(x => x.Key is string && ((string)x.Key).StartsWith(prefix));
}
protected override void RemoveEntry(string key)
{
_cache.Remove(key);
}
protected override object GetEntry(string key)
{
return _cache.Get(key);
}
#region Lock
protected override IDisposable ReadLock
{
get { return new ReadLock(_locker); }
}
protected override IDisposable WriteLock
{
get { return new WriteLock(_locker); }
}
#endregion
#region Get
/// <summary>
/// Gets (and adds if necessary) an item from the cache with all of the default parameters
/// </summary>
/// <param name="cacheKey"></param>
/// <param name="getCacheItem"></param>
/// <returns></returns>
public override object GetCacheItem(string cacheKey, Func<object> getCacheItem)
{
return GetCacheItem(cacheKey, getCacheItem, null, dependentFiles: null);
}
/// <summary>
/// This overload is here for legacy purposes
/// </summary>
/// <param name="cacheKey"></param>
/// <param name="getCacheItem"></param>
/// <param name="timeout"></param>
/// <param name="isSliding"></param>
/// <param name="priority"></param>
/// <param name="removedCallback"></param>
/// <param name="dependency"></param>
/// <returns></returns>
internal object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null)
{
cacheKey = GetCacheKey(cacheKey);
// NOTE - because we don't know what getCacheItem does, how long it will take and whether it will hang,
// getCacheItem should run OUTSIDE of the global application lock else we run into lock contention and
// nasty performance issues.
// So.... we insert a Lazy<object> in the cache while holding the global application lock, and then rely
// on the Lazy lock to ensure that getCacheItem runs once and everybody waits on it, while the global
// application lock has been released.
// NOTE
// The Lazy value creation may produce a null value.
// Must make sure (for backward compatibility) that we pretend they are not in the cache.
// So if we find an entry in the cache that already has its value created and is null,
// pretend it was not there. If value is not already created, wait... and return null, that's
// what prior code did.
// NOTE
// The Lazy value creation may throw.
// So... the null value _will_ be in the cache but never returned
Lazy<object> result;
// Fast!
// Only one thread can enter an UpgradeableReadLock at a time, but it does not prevent other
// threads to enter a ReadLock in the meantime -- only upgrading to WriteLock will prevent all
// reads. We first try with a normal ReadLock for maximum concurrency and take the penalty of
// having to re-lock in case there's no value. Would need to benchmark to figure out whether
// it's worth it, though...
using (new ReadLock(_locker))
{
result = _cache.Get(cacheKey) as Lazy<object>; // null if key not found
}
var value = result == null ? null : GetSafeLazyValue(result);
if (value != null) return value;
using (var lck = new UpgradeableReadLock(_locker))
{
result = _cache.Get(cacheKey) as Lazy<object>; // null if key not found
// cannot create value within the lock, so if result.IsValueCreated is false, just
// do nothing here - means that if creation throws, a race condition could cause
// more than one thread to reach the return statement below and throw - accepted.
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
{
result = GetSafeLazy(getCacheItem);
var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration);
lck.UpgradeToWriteLock();
//NOTE: 'Insert' on System.Web.Caching.Cache actually does an add or update!
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
}
}
// using GetSafeLazy and GetSafeLazyValue ensures that we don't cache
// exceptions (but try again and again) and silently eat them - however at
// some point we have to report them - so need to re-throw here
// this does not throw anymore
//return result.Value;
value = result.Value; // will not throw (safe lazy)
var eh = value as ExceptionHolder;
if (eh != null) throw new Exception("Exception while creating a value.", eh.Exception); // throw once!
return value;
}
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
CacheDependency dependency = null;
if (dependentFiles != null && dependentFiles.Any())
{
dependency = new CacheDependency(dependentFiles);
}
return GetCacheItem(cacheKey, getCacheItem, timeout, isSliding, priority, removedCallback, dependency);
}
#endregion
#region Insert
/// <summary>
/// This overload is here for legacy purposes
/// </summary>
/// <param name="cacheKey"></param>
/// <param name="getCacheItem"></param>
/// <param name="timeout"></param>
/// <param name="isSliding"></param>
/// <param name="priority"></param>
/// <param name="removedCallback"></param>
/// <param name="dependency"></param>
internal void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null)
{
// NOTE - here also we must insert a Lazy<object> but we can evaluate it right now
// and make sure we don't store a null value.
var result = GetSafeLazy(getCacheItem);
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
if (value == null) return; // do not store null values (backward compat)
cacheKey = GetCacheKey(cacheKey);
var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration);
using (new WriteLock(_locker))
{
//NOTE: 'Insert' on System.Web.Caching.Cache actually does an add or update!
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
}
}
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
CacheDependency dependency = null;
if (dependentFiles != null && dependentFiles.Any())
{
dependency = new CacheDependency(dependentFiles);
}
InsertCacheItem(cacheKey, getCacheItem, timeout, isSliding, priority, removedCallback, dependency);
}
#endregion
}
}

View File

@@ -1,68 +1,68 @@
using System;
using System.Collections.Generic;
namespace Umbraco.Core.Cache
{
/// <summary>
/// An interface for implementing a basic cache provider
/// </summary>
public interface ICacheProvider
{
/// <summary>
/// Removes all items from the cache.
/// </summary>
void ClearAllCache();
/// <summary>
/// Removes an item from the cache, identified by its key.
/// </summary>
/// <param name="key">The key of the item.</param>
void ClearCacheItem(string key);
/// <summary>
/// Removes items from the cache, of a specified type.
/// </summary>
/// <param name="typeName">The name of the type to remove.</param>
/// <remarks>
/// <para>If the type is an interface, then all items of a type implementing that interface are
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
/// the specified type are not removed).</para>
/// <para>Performs a case-sensitive search.</para>
/// </remarks>
void ClearCacheObjectTypes(string typeName);
/// <summary>
/// Removes items from the cache, of a specified type.
/// </summary>
/// <typeparam name="T">The type of the items to remove.</typeparam>
/// <remarks>If the type is an interface, then all items of a type implementing that interface are
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
/// the specified type are not removed).</remarks>
void ClearCacheObjectTypes<T>();
/// <summary>
/// Removes items from the cache, of a specified type, satisfying a predicate.
/// </summary>
/// <typeparam name="T">The type of the items to remove.</typeparam>
/// <param name="predicate">The predicate to satisfy.</param>
/// <remarks>If the type is an interface, then all items of a type implementing that interface are
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
/// the specified type are not removed).</remarks>
void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate);
void ClearCacheByKeySearch(string keyStartsWith);
void ClearCacheByKeyExpression(string regexString);
IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith);
IEnumerable<object> GetCacheItemsByKeyExpression(string regexString);
/// <summary>
/// Returns an item with a given key
/// </summary>
/// <param name="cacheKey"></param>
/// <returns></returns>
object GetCacheItem(string cacheKey);
object GetCacheItem(string cacheKey, Func<object> getCacheItem);
}
}
using System;
using System.Collections.Generic;
namespace Umbraco.Core.Cache
{
/// <summary>
/// An interface for implementing a basic cache provider
/// </summary>
public interface ICacheProvider
{
/// <summary>
/// Removes all items from the cache.
/// </summary>
void ClearAllCache();
/// <summary>
/// Removes an item from the cache, identified by its key.
/// </summary>
/// <param name="key">The key of the item.</param>
void ClearCacheItem(string key);
/// <summary>
/// Removes items from the cache, of a specified type.
/// </summary>
/// <param name="typeName">The name of the type to remove.</param>
/// <remarks>
/// <para>If the type is an interface, then all items of a type implementing that interface are
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
/// the specified type are not removed).</para>
/// <para>Performs a case-sensitive search.</para>
/// </remarks>
void ClearCacheObjectTypes(string typeName);
/// <summary>
/// Removes items from the cache, of a specified type.
/// </summary>
/// <typeparam name="T">The type of the items to remove.</typeparam>
/// <remarks>If the type is an interface, then all items of a type implementing that interface are
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
/// the specified type are not removed).</remarks>
void ClearCacheObjectTypes<T>();
/// <summary>
/// Removes items from the cache, of a specified type, satisfying a predicate.
/// </summary>
/// <typeparam name="T">The type of the items to remove.</typeparam>
/// <param name="predicate">The predicate to satisfy.</param>
/// <remarks>If the type is an interface, then all items of a type implementing that interface are
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
/// the specified type are not removed).</remarks>
void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate);
void ClearCacheByKeySearch(string keyStartsWith);
void ClearCacheByKeyExpression(string regexString);
IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith);
IEnumerable<object> GetCacheItemsByKeyExpression(string regexString);
/// <summary>
/// Returns an item with a given key
/// </summary>
/// <param name="cacheKey"></param>
/// <returns></returns>
object GetCacheItem(string cacheKey);
object GetCacheItem(string cacheKey, Func<object> getCacheItem);
}
}

View File

@@ -1,33 +1,18 @@
using System;
using Umbraco.Core.Composing;
namespace Umbraco.Core.Cache
{
/// <summary>
/// The IcacheRefresher Interface is used for loadbalancing.
///
/// </summary>
public interface ICacheRefresher : IDiscoverable
{
Guid RefresherUniqueId { get; }
string Name { get; }
void RefreshAll();
void Refresh(int id);
void Remove(int id);
void Refresh(Guid id);
}
/// <summary>
/// Strongly type cache refresher that is able to refresh cache of real instances of objects as well as IDs
/// </summary>
/// <typeparam name="T"></typeparam>
/// <remarks>
/// This is much better for performance when we're not running in a load balanced environment so we can refresh the cache
/// against a already resolved object instead of looking the object back up by id.
/// </remarks>
interface ICacheRefresher<T> : ICacheRefresher
{
void Refresh(T instance);
void Remove(T instance);
}
}
using umbraco.interfaces;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Strongly type cache refresher that is able to refresh cache of real instances of objects as well as IDs
/// </summary>
/// <typeparam name="T"></typeparam>
/// <remarks>
/// This is much better for performance when we're not running in a load balanced environment so we can refresh the cache
/// against a already resolved object instead of looking the object back up by id.
/// </remarks>
interface ICacheRefresher<T> : ICacheRefresher
{
void Refresh(T instance);
void Remove(T instance);
}
}

View File

@@ -1,14 +1,16 @@
namespace Umbraco.Core.Cache
{
/// <summary>
/// A cache refresher that supports refreshing or removing cache based on a custom Json payload
/// </summary>
interface IJsonCacheRefresher : ICacheRefresher
{
/// <summary>
/// Refreshes, clears, etc... any cache based on the information provided in the json
/// </summary>
/// <param name="json"></param>
void Refresh(string json);
}
}
using umbraco.interfaces;
namespace Umbraco.Core.Cache
{
/// <summary>
/// A cache refresher that supports refreshing or removing cache based on a custom Json payload
/// </summary>
interface IJsonCacheRefresher : ICacheRefresher
{
/// <summary>
/// Refreshes, clears, etc... any cache based on the information provided in the json
/// </summary>
/// <param name="jsonPayload"></param>
void Refresh(string jsonPayload);
}
}

View File

@@ -1,14 +1,16 @@
namespace Umbraco.Core.Cache
using umbraco.interfaces;
namespace Umbraco.Core.Cache
{
/// <summary>
/// A cache refresher that supports refreshing cache based on a custom payload
/// </summary>
interface IPayloadCacheRefresher<TPayload> : IJsonCacheRefresher
interface IPayloadCacheRefresher : IJsonCacheRefresher
{
/// <summary>
/// Refreshes, clears, etc... any cache based on the information provided in the payload
/// </summary>
/// <param name="payloads"></param>
void Refresh(TPayload[] payloads);
/// <param name="payload"></param>
void Refresh(object payload);
}
}

View File

@@ -1,13 +1,33 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.Cache
{
internal interface IRepositoryCachePolicy<TEntity, TId>
where TEntity : class, IEntity
where TEntity : class, IAggregateRoot
{
// note:
// at the moment each repository instance creates its corresponding cache policy instance
// we could reduce allocations by using static cache policy instances but then we would need
// to modify all methods here to pass the repository and cache eg:
//
// TEntity Get(TRepository repository, IRuntimeCacheProvider cache, TId id);
//
// it is not *that* complicated but then RepositoryBase needs to have a TRepository generic
// type parameter and it all becomes convoluted - keeping it simple for the time being.
/// <summary>
/// Creates a scoped version of this cache policy.
/// </summary>
/// <param name="runtimeCache">The global isolated runtime cache for this policy.</param>
/// <param name="scope">The scope.</param>
/// <remarks>When a policy is scoped, it means that it has been created with a scoped
/// isolated runtime cache, and now it needs to be wrapped into something that can apply
/// changes to the global isolated runtime cache.</remarks>
IRepositoryCachePolicy<TEntity, TId> Scoped(IRuntimeCacheProvider runtimeCache, IScope scope);
/// <summary>
/// Gets an entity from the cache, else from the repository.
/// </summary>
@@ -74,4 +94,4 @@ namespace Umbraco.Core.Cache
/// </summary>
void ClearAll();
}
}
}

View File

@@ -1,35 +1,35 @@
using System;
using System.Runtime.Caching;
using System.Text;
using System.Web.Caching;
using CacheItemPriority = System.Web.Caching.CacheItemPriority;
namespace Umbraco.Core.Cache
{
/// <summary>
/// An abstract class for implementing a runtime cache provider
/// </summary>
/// <remarks>
/// </remarks>
public interface IRuntimeCacheProvider : ICacheProvider
{
object GetCacheItem(
string cacheKey,
Func<object> getCacheItem,
TimeSpan? timeout,
bool isSliding = false,
CacheItemPriority priority = CacheItemPriority.Normal,
CacheItemRemovedCallback removedCallback = null,
string[] dependentFiles = null);
void InsertCacheItem(
string cacheKey,
Func<object> getCacheItem,
TimeSpan? timeout = null,
bool isSliding = false,
CacheItemPriority priority = CacheItemPriority.Normal,
CacheItemRemovedCallback removedCallback = null,
string[] dependentFiles = null);
}
}
using System;
using System.Runtime.Caching;
using System.Text;
using System.Web.Caching;
using CacheItemPriority = System.Web.Caching.CacheItemPriority;
namespace Umbraco.Core.Cache
{
/// <summary>
/// An abstract class for implementing a runtime cache provider
/// </summary>
/// <remarks>
/// </remarks>
public interface IRuntimeCacheProvider : ICacheProvider
{
object GetCacheItem(
string cacheKey,
Func<object> getCacheItem,
TimeSpan? timeout,
bool isSliding = false,
CacheItemPriority priority = CacheItemPriority.Normal,
CacheItemRemovedCallback removedCallback = null,
string[] dependentFiles = null);
void InsertCacheItem(
string cacheKey,
Func<object> getCacheItem,
TimeSpan? timeout = null,
bool isSliding = false,
CacheItemPriority priority = CacheItemPriority.Normal,
CacheItemRemovedCallback removedCallback = null,
string[] dependentFiles = null);
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Concurrent;
namespace Umbraco.Core.Cache
@@ -7,7 +7,7 @@ namespace Umbraco.Core.Cache
/// Used to get/create/manipulate isolated runtime cache
/// </summary>
/// <remarks>
/// This is useful for repository level caches to ensure that cache lookups by key are fast so
/// This is useful for repository level caches to ensure that cache lookups by key are fast so
/// that the repository doesn't need to search through all keys on a global scale.
/// </remarks>
public class IsolatedRuntimeCache
@@ -29,7 +29,7 @@ namespace Umbraco.Core.Cache
/// Returns an isolated runtime cache for a given type
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
/// <returns></returns>
public IRuntimeCacheProvider GetOrCreateCache<T>()
{
return _isolatedCache.GetOrAdd(typeof(T), type => CacheFactory(type));
@@ -38,7 +38,7 @@ namespace Umbraco.Core.Cache
/// <summary>
/// Returns an isolated runtime cache for a given type
/// </summary>
/// <returns></returns>
/// <returns></returns>
public IRuntimeCacheProvider GetOrCreateCache(Type type)
{
return _isolatedCache.GetOrAdd(type, t => CacheFactory(t));
@@ -88,4 +88,4 @@ namespace Umbraco.Core.Cache
}
}
}
}
}

View File

@@ -1,29 +1,19 @@
using Umbraco.Core.Sync;
namespace Umbraco.Core.Cache
{
/// <summary>
/// A base class for "json" cache refreshers.
/// </summary>
/// <typeparam name="TInstanceType">The actual cache refresher type.</typeparam>
/// <remarks>The actual cache refresher type is used for strongly typed events.</remarks>
public abstract class JsonCacheRefresherBase<TInstanceType> : CacheRefresherBase<TInstanceType>, IJsonCacheRefresher
where TInstanceType : class, ICacheRefresher
{
/// <summary>
/// Initializes a new instance of the <see cref="JsonCacheRefresherBase{TInstanceType}"/>.
/// </summary>
/// <param name="cacheHelper">A cache helper.</param>
protected JsonCacheRefresherBase(CacheHelper cacheHelper) : base(cacheHelper)
{ }
/// <summary>
/// Refreshes as specified by a json payload.
/// </summary>
/// <param name="json">The json payload.</param>
public virtual void Refresh(string json)
{
OnCacheUpdated(This, new CacheRefresherEventArgs(json, MessageType.RefreshByJson));
}
}
}
using Umbraco.Core.Sync;
using umbraco.interfaces;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Provides a base class for "json" cache refreshers.
/// </summary>
/// <typeparam name="TInstance">The actual cache refresher type.</typeparam>
/// <remarks>Ensures that the correct events are raised when cache refreshing occurs.</remarks>
public abstract class JsonCacheRefresherBase<TInstance> : CacheRefresherBase<TInstance>, IJsonCacheRefresher
where TInstance : ICacheRefresher
{
public virtual void Refresh(string json)
{
OnCacheUpdated(Instance, new CacheRefresherEventArgs(json, MessageType.RefreshByJson));
}
}
}

View File

@@ -1,59 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.Cache
{
internal class NoCacheRepositoryCachePolicy<TEntity, TId> : IRepositoryCachePolicy<TEntity, TId>
where TEntity : class, IEntity
{
private NoCacheRepositoryCachePolicy() { }
public static NoCacheRepositoryCachePolicy<TEntity, TId> Instance { get; } = new NoCacheRepositoryCachePolicy<TEntity, TId>();
public IRepositoryCachePolicy<TEntity, TId> Scoped(IRuntimeCacheProvider runtimeCache, IScope scope)
{
throw new NotImplementedException();
}
public TEntity Get(TId id, Func<TId, TEntity> performGet, Func<TId[], IEnumerable<TEntity>> performGetAll)
{
return performGet(id);
}
public TEntity GetCached(TId id)
{
return null;
}
public bool Exists(TId id, Func<TId, bool> performExists, Func<TId[], IEnumerable<TEntity>> performGetAll)
{
return performExists(id);
}
public void Create(TEntity entity, Action<TEntity> persistNew)
{
persistNew(entity);
}
public void Update(TEntity entity, Action<TEntity> persistUpdated)
{
persistUpdated(entity);
}
public void Delete(TEntity entity, Action<TEntity> persistDeleted)
{
persistDeleted(entity);
}
public TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> performGetAll)
{
return performGetAll(ids).ToArray();
}
public void ClearAll()
{ }
}
}

View File

@@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.Cache
{
internal class NoRepositoryCachePolicy<TEntity, TId> : IRepositoryCachePolicy<TEntity, TId>
where TEntity : class, IAggregateRoot
{
private static readonly NoRepositoryCachePolicy<TEntity, TId> StaticInstance = new NoRepositoryCachePolicy<TEntity, TId>();
private NoRepositoryCachePolicy()
{ }
public static NoRepositoryCachePolicy<TEntity, TId> Instance { get { return StaticInstance; } }
public IRepositoryCachePolicy<TEntity, TId> Scoped(IRuntimeCacheProvider runtimeCache, IScope scope)
{
throw new NotImplementedException();
}
public TEntity Get(TId id, Func<TId, TEntity> performGet, Func<TId[], IEnumerable<TEntity>> performGetAll)
{
return performGet(id);
}
public TEntity GetCached(TId id)
{
return null;
}
public bool Exists(TId id, Func<TId, bool> performExists, Func<TId[], IEnumerable<TEntity>> performGetAll)
{
return performExists(id);
}
public void Create(TEntity entity, Action<TEntity> persistNew)
{
persistNew(entity);
}
public void Update(TEntity entity, Action<TEntity> persistUpdated)
{
persistUpdated(entity);
}
public void Delete(TEntity entity, Action<TEntity> persistDeleted)
{
persistDeleted(entity);
}
public TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> performGetAll)
{
return performGetAll(ids).ToArray();
}
public void ClearAll()
{ }
}
}

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