Compare commits

..

1 Commits

Author SHA1 Message Date
Bjarke Berg aed3d1f553 #3539 - use culture to find the url in the actual redirect 2019-01-08 10:59:14 +01:00
2893 changed files with 79797 additions and 70874 deletions
+1 -4
View File
@@ -34,7 +34,4 @@ dotnet_naming_style.prefix_underscore.required_prefix = _
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_var_elsewhere = true:suggestion
csharp_prefer_braces = false : none
[*.{js,less}]
trim_trailing_whitespace = false
csharp_prefer_braces = false : none
+122 -170
View File
@@ -1,176 +1,14 @@
# Umbraco Cms Build
Umbraco Cms Build
--
----
## Are you sure?
# Quick!
In order to use Umbraco as a CMS and build your website with it, you should not build it yourself. If you're reading this then you're trying to contribute to Umbraco or you're debugging a complex issue.
- Are you about to create a pull request for Umbraco?
- Are you trying to get to the bottom of a problem in your existing Umbraco installation?
If the answer is yes, please read on. Otherwise, make sure to head on over [to the download page](https://our.umbraco.com/download) and start using Umbraco CMS as intended.
**Table of contents**
[Building from source](#building-from-source)
* [The quick build](#quick)
* [Build infrastructure](#build-infrastructure)
* [Properties](#properties)
* [GetUmbracoVersion](#getumbracoversion)
* [SetUmbracoVersion](#setumbracoversion)
* [Build](#build)
* [Build-UmbracoDocs](#build-umbracodocs)
* [Verify-NuGet](#verify-nuget)
* [Cleaning up](#cleaning-up)
[Azure DevOps](#azure-devops)
[Quirks](#quirks)
* [Powershell quirks](#powershell-quirks)
* [Git quirks](#git-quirks)
## Building from source
Did you read ["Are you sure"](#are-you-sure)?
### Quick!
To build Umbraco, fire up PowerShell and move to Umbraco's repository root (the directory that contains `src`, `build`, `LICENSE.md`...). There, trigger the build with the following command:
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
You might run into [Powershell quirks](#powershell-quirks).
### Build Infrastructure
The Umbraco Build infrastructure relies on a PowerShell object. The object can be retrieved with:
$ubuild = build/build.ps1 -get
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.
#### Properties
The object exposes the following properties:
* `SolutionRoot`: the absolute path to the solution root
* `VisualStudio`: a Visual Studio object (see below)
* `NuGet`: the absolute path to the NuGet executable
* `Zip`: the absolute path to the 7Zip executable
* `VsWhere`: the absolute path to the VsWhere executable
* `NodePath`: the absolute path to the Node install
* `NpmPath`: the absolute path to the Npm install
The Visual Studio object is `null` when Visual Studio has not been detected (eg on VSTS). When not null, the object exposes the following properties:
* `Path`: Visual Studio installation path (eg some place under `Program Files`)
* `Major`: Visual Studio major version (eg `15` for VS 2017)
* `Minor`: Visual Studio minor version
* `MsBUild`: the absolute path to the MsBuild executable
#### GetUmbracoVersion
Gets an object representing the current Umbraco version. Example:
$v = $ubuild.GetUmbracoVersion()
Write-Host $v.Semver
The object exposes the following properties:
* `Semver`: the semver object representing the version
* `Release`: the main part of the version (eg `7.6.33`)
* `Comment`: the pre release part of the version (eg `alpha02`)
* `Build`: the build number part of the version (eg `1234`)
#### SetUmbracoVersion
Modifies Umbraco files with the new version.
>This entirely replaces the legacy `UmbracoVersion.txt` file. Do *not* edit version infos in files.
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")
#### Build
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()
Some log files, such as MsBuild logs, are produced in `build.tmp` too. The `build` directory should remain clean during a build.
**Note: web.config**
Building Umbraco requires a clean `web.config` file in the `Umbraco.Web.UI` project. If a `web.config` file already exists, the `pre-build` task (see below) will save it as `web.config.temp-build` and replace it with a clean copy of `web.Template.config`. The original file is replaced once it is safe to do so, by the `pre-packages` task.
#### Build-UmbracoDocs
Builds umbraco documentation. Temporary files are generated in `build.tmp` while the actual artifacts (docs...) are produced in `build.out`. Example:
Build-UmbracoDocs
Some log files, such as MsBuild logs, are produced in `build.tmp` too. The `build` directory should remain clean during a build.
#### Verify-NuGet
Verifies that projects all require the same version of their dependencies, and that NuSpec files require versions that are consistent with projects. Example:
Verify-NuGet
### Cleaning up
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.
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 media files and views around, but in most cases, it will be enough.
To perform a more complete clear, you will want to also delete the content of the media, views, scripts... directories.
The following command will force remove all untracked files and directories, whether they are ignored by Git or not. Combined with `git reset` it can recreate a pristine working directory.
git clean -xdf .
For git documentation see:
* git [clean](<https://git-scm.com/docs/git-clean>)
* git [reset](<https://git-scm.com/docs/git-reset>)
## Azure DevOps
Umbraco uses Azure DevOps for continuous integration, nightly builds and release builds. The Umbraco CMS project on DevOps [is available for anonymous users](https://umbraco.visualstudio.com/Umbraco%20Cms).
DevOps uses the `Build-Umbraco` command several times, each time passing a different *target* parameter. The supported targets are:
* `pre-build`: prepares the build
* `compile-belle`: compiles Belle
* `compile-umbraco`: compiles Umbraco
* `pre-tests`: prepares the tests
* `compile-tests`: compiles the tests
* `pre-packages`: prepares the packages
* `pkg-zip`: creates the zip files
* `pre-nuget`: prepares NuGet packages
* `pkg-nuget`: creates NuGet packages
All these targets are executed when `Build-Umbraco` is invoked without a parameter (or with the `all` parameter). On VSTS, compilations (of Umbraco and tests) are performed by dedicated DevOps tasks. Similarly, creating the NuGet packages is also performed by dedicated DevOps tasks.
Finally, the produced artifacts are published in two containers that can be downloaded from DevOps: `zips` contains the zip files while `nuget` contains the NuGet packages.
>During a DevOps build, some environment `UMBRACO_*` variables are exported by the `pre-build` target and can be reused in other targets *and* in DevOps tasks. The `UMBRACO_TMP` environment variable is used in `Umbraco.Tests` to disable some tests that have issues with DevOps at the moment.
## Quirks
### PowerShell Quirks
## PowerShell Quirks
There is a good chance that running `build.ps1` ends up in error, with messages such as
@@ -207,6 +45,120 @@ The best solution is to unblock the Zip file before un-zipping: right-click the
PS> Get-ChildItem -Recurse *.* | Unblock-File
### Git Quirks
## Git Quirks
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).
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
The Umbraco Build infrastructure relies on a PowerShell object. The object can be retrieved with:
$ubuild = build/build.ps1 -get
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.
## Properties
The object exposes the following properties:
* `SolutionRoot`: the absolute path to the solution root
* `VisualStudio`: a Visual Studio object (see below)
* `NuGet`: the absolute path to the NuGet executable
* `Zip`: the absolute path to the 7Zip executable
* `VsWhere`: the absolute path to the VsWhere executable
* `NodePath`: the absolute path to the Node install
* `NpmPath`: the absolute path to the Npm install
The Visual Studio object is `null` when Visual Studio has not been detected (eg on VSTS). When not null, the object exposes the following properties:
* `Path`: Visual Studio installation path (eg some place under `Program Files`)
* `Major`: Visual Studio major version (eg `15` for VS 2017)
* `Minor`: Visual Studio minor version
* `MsBUild`: the absolute path to the MsBuild executable
## GetUmbracoVersion
Gets an object representing the current Umbraco version. Example:
$v = $ubuild.GetUmbracoVersion()
Write-Host $v.Semver
The object exposes the following properties:
* `Semver`: the semver object representing the version
* `Release`: the main part of the version (eg `7.6.33`)
* `Comment`: the pre release part of the version (eg `alpha02`)
* `Build`: the build number part of the version (eg `1234`)
## SetUmbracoVersion
Modifies Umbraco files with the new version.
>This entirely replaces the legacy `UmbracoVersion.txt` file. Do *not* edit version infos in files.
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")
## Build
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()
Some log files, such as MsBuild logs, are produced in `build.tmp` too. The `build` directory should remain clean during a build.
### web.config
Building Umbraco requires a clean `web.config` file in the `Umbraco.Web.UI` project. If a `web.config` file already exists, the `pre-build` task (see below) will save it as `web.config.temp-build` and replace it with a clean copy of `web.Template.config`. The original file is replaced once it is safe to do so, by the `pre-packages` task.
## Build-UmbracoDocs
Builds umbraco documentation. Temporary files are generated in `build.tmp` while the actual artifacts (docs...) are produced in `build.out`. Example:
Build-UmbracoDocs
Some log files, such as MsBuild logs, are produced in `build.tmp` too. The `build` directory should remain clean during a build.
## Verify-NuGet
Verifies that projects all require the same version of their dependencies, and that NuSpec files require versions that are consistent with projects. Example:
Verify-NuGet
# VSTS
Continuous integration, nightly builds and release builds run on VSTS.
VSTS uses the `Build-Umbraco` command several times, each time passing a different *target* parameter. The supported targets are:
* `pre-build`: prepares the build
* `compile-belle`: compiles Belle
* `compile-umbraco`: compiles Umbraco
* `pre-tests`: prepares the tests
* `compile-tests`: compiles the tests
* `pre-packages`: prepares the packages
* `pkg-zip`: creates the zip files
* `pre-nuget`: prepares NuGet packages
* `pkg-nuget`: creates NuGet packages
All these targets are executed when `Build-Umbraco` is invoked without a parameter (or with the `all` parameter). On VSTS, compilations (of Umbraco and tests) are performed by dedicated VSTS tasks. Similarly, creating the NuGet packages is also performed by dedicated VSTS tasks.
Finally, the produced artifacts are published in two containers that can be downloaded from VSTS: `zips` contains the zip files while `nuget` contains the NuGet packages.
>During a VSTS build, some environment `UMBRACO_*` variables are exported by the `pre-build` target and can be reused in other targets *and* in VSTS tasks. The `UMBRACO_TMP` environment variable is used in `Umbraco.Tests` to disable some tests that have issues with VSTS at the moment.
# Notes
*This part needs to be cleaned up*
Nightlies should use some sort of build number.
We should increment versions as soon as a version is released. Ie, as soon as `7.6.33` is released, we should `Set-UmbracoVersion 7.6.34-alpha` and push.
NuGet / NuSpec consistency checks are performed in tests. We should move it so it is done as part of the PowerShell script even before we try to compile and run the tests.
/eof
+34
View File
@@ -0,0 +1,34 @@
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>)
+97 -187
View File
@@ -1,187 +1,97 @@
# Contributing to Umbraco CMS
👍🎉 First off, thanks for taking the time to contribute! 🎉👍
The following is a set of guidelines for contributing to Umbraco CMS.
These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
Remember, we're a friendly bunch and are happy with whatever contribution you might provide. Below are guidelines for success that we've gathered over the years. If you choose to ignore them then we still love you 💖.
**Code of conduct**
This project and everyone participating in it is governed by the [our Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [Sebastiaan Janssen - sj@umbraco.dk](mailto:sj@umbraco.dk).
**Table of contents**
[Contributing code changes](#contributing-code-changes)
* [Guidelines for contributions we welcome](#guidelines-for-contributions-we-welcome)
* [What can I start with?](#what-can-i-start-with)
* [How do I begin?](#how-do-i-begin)
* [Pull requests](#pull-requests)
[Reviews](#reviews)
* [Styleguides](#styleguides)
* [The PR team](#the-pr-team)
* [Questions?](#questions)
[Working with the code](#working-with-the-code)
* [Building Umbraco from source code](#building-umbraco-from-source-code)
* [Working with the source code](#working-with-the-source-code)
* [Making changes after the PR was opened](#making-changes-after-the-pr-was-opened)
* [Which branch should I target for my contributions?](#which-branch-should-i-target-for-my-contributions)
* [Keeping your Umbraco fork in sync with the main repository](#keeping-your-umbraco-fork-in-sync-with-the-main-repository)
## Contributing code changes
This document gives you a quick overview on how to get started.
### Guidelines for contributions we welcome
Not all changes are wanted, so on occassion we might close a PR without merging it. We will give you feedback why we can't accept your changes and we'll be nice about it, thanking you for spending your valuable time.
We have [documented what we consider small and large changes](CONTRIBUTION_GUIDELINES.md). Make sure to talk to us before making large changes.
Remember, if an issue is in the `Up for grabs` list or you've asked for some feedback before you sent us a PR, your PR will not be closed as unwanted.
### What can I start with?
Unsure where to begin contributing to Umbraco? You can start by looking through [these `Up for grabs` issues](https://github.com/umbraco/Umbraco-CMS/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3Acommunity%2Fup-for-grabs+)
### How do I begin?
Great question! The short version goes like this:
* **Fork** - create a fork of [`Umbraco-CMS` on GitHub](https://github.com/umbraco/Umbraco-CMS)
![Fork the repository](img/forkrepository.png)
* **Clone** - when GitHub has created your fork, you can clone it in your favorite Git tool
![Clone the fork](img/clonefork.png)
* **Build** - build your fork of Umbraco locally as described in [building Umbraco from source code](BUILD.md)
* **Change** - make your changes, experiment, have fun, explore and learn, and don't be afraid. We welcome all contributions and will [happily give feedback](#questions)
* **Commit** - done? Yay! 🎉 **Important:** create a new branch now and name it after the issue you're fixing, we usually follow the format: `temp-12345`. This means it's a temporary branch for the particular issue you're working on, in this case `12345`. When you have a branch, commit your changes. Don't commit to `v8/dev`, create a new branch first.
* **Push** - great, now you can push the changes up to your fork on GitHub
* **Create pull request** - exciting! You're ready to show us your changes (or not quite ready, you just need some feedback to progress). GitHub has picked up on the new branch you've pushed and will offer to create a Pull Request. Click that green button and away you go.
![Create a pull request](img/createpullrequest.png)
### Pull requests
The most successful pull requests usually look a like this:
* Fill in the required template
* Include screenshots and animated GIFs in your pull request whenever possible.
* Unit tests, while optional are awesome, thank you!
* New code is commented with documentation from which [the reference documentation](https://our.umbraco.com/documentation/Reference/) is generated
Again, these are guidelines, not strict requirements.
## Reviews
You've sent us your first contribution, congratulations! Now what?
The [pull request team](#the-pr-team) can now start reviewing your proposed changes and give you feedback on them. If it's not perfect, we'll either fix up what we need or we can request you to make some additional changes.
We have [a process in place which you can read all about](REVIEW_PROCESS.md). The very abbreviated version is:
- Your PR will get a reply within 48 hours
- An in-depth reply will be added within at most 2 weeks
- The PR will be either merged or rejected within at most 4 weeks
- Sometimes it is difficult to meet these timelines and we'll talk to you
### Styleguides
To be honest, we don't like rules very much. We trust you have the best of intentions and we encourage you to create working code. If it doesn't look perfect then we'll happily help clean it up.
That said, the Umbraco development team likes to follow the hints that ReSharper gives us (no problem if you don't have this installed) and we've added a `.editorconfig` file so that Visual Studio knows what to do with whitespace, line endings, etc.
### The PR team
The pull request team consists of a member of Umbraco HQ, [Sebastiaan](https://github.com/nul800sebastiaan), who gets assistance from the following community members
- [Anders Bjerner](https://github.com/abjerner)
- [Dave Woestenborghs](https://github.com/dawoe)
- [Emma Burstow](https://github.com/emmaburstow)
- [Poornima Nayar](https://github.com/poornimanayar)
These wonderful volunteers will provide you with a first reply to your PR, review and test out your changes and might ask more questions. After that they'll let Umbraco HQ know if everything seems okay.
### Questions?
You can get in touch with [the PR team](#the-pr-team) in multiple ways, we love open conversations and we are a friendly bunch. No question you have is stupid. Any questions you have usually helps out multiple people with the same question. Ask away:
- If there's an existing issue on the issue tracker then that's a good place to leave questions and discuss how to start or move forward
- Unsure where to start? Did something not work as expected? Try leaving a note in the ["Contributing to Umbraco"](https://our.umbraco.com/forum/contributing-to-umbraco-cms/) forum, the team monitors that one closely
## Working with the code
### Building Umbraco from source code
In order to build the Umbraco source code locally, first make sure you have the following installed.
* Visual Studio 2017 v15.9.7+
* Node v10+
* npm v6.4.1+
The easiest way to get started is to run `build.ps1` which will build both the backoffice (also known as "Belle") and the Umbraco core. You can then easily start debugging from Visual Studio, or if you need to debug Belle you can run `gulp dev` in `src\Umbraco.Web.UI.Client`. See [this page](BUILD.md) for more details.
Alternatively, you can open `src\umbraco.sln` in Visual Studio 2017 (version 15.9.7 or higher, [the community edition is free](https://www.visualstudio.com/thank-you-downloading-visual-studio/?sku=Community&rel=15) for you to use to contribute to Open Source projects). In Visual Studio, find the Task Runner Explorer (in the View menu under Other Windows) and run the build task under the gulpfile.
![Gulp build in Visual Studio](img/gulpbuild.png)
After this build completes, you should be able to hit `F5` in Visual Studio to build and run the project. A IISExpress webserver will start and the Umbraco installer will pop up in your browser, follow the directions there to get a working Umbraco install up and running.
### Working with the source code
Some parts of our source code are over 10 years old now. And when we say "old", we mean "mature" of course!
There's two big areas that you should know about:
1. The Umbraco backoffice is a extensible AngularJS app and requires you to run a `gulp dev` command while you're working with it, so changes are copied over to the appropriate directories and you can refresh your browser to view the results of your changes.
You may need to run the following commands to set up gulp properly:
```
npm cache clean --force
npm install
npm run build
```
2. "The rest" is a C# based codebase, which is mostly ASP.NET MVC based. You can make changes, build them in Visual Studio, and hit `F5` to see the result.
To find the general areas of something you're looking to fix or improve, have a look at the following two parts of the API documentation.
* [The AngularJS based backoffice files](https://our.umbraco.com/apidocs/ui/#/api) (to be found in `src\Umbraco.Web.UI.Client\src`)
* [The C# application](https://our.umbraco.com/apidocs/csharp/)
### Which branch should I target for my contributions?
We like to use [Gitflow as much as possible](https://jeffkreeftmeijer.com/git-flow/), don't worry if you are not familiar with it. The most important thing you need to know is that when you fork the Umbraco repository, the default branch is set to something, usually `v8/dev`. Whatever the default is, that's where we'd like you to target your contributions.
![Which branch should I target?](img/defaultbranch.png)
### Making changes after the PR was opened
If you make the corrections we ask for in the same branch and push them to your fork again, the pull request automatically updates with the additional commit(s) so we can review it again. If all is well, we'll merge the code and your commits are forever part of Umbraco!
### Keeping your Umbraco fork in sync with the main repository
We recommend you sync with our repository before you submit your pull request. That way, you can fix any potential merge conflicts and make our lives a little bit easier.
Also, if you've submitted a pull request three weeks ago and want to work on something new, you'll want to get the latest code to build against of course.
To sync your fork with this original one, you'll have to add the upstream url, you only have to do this once:
```
git remote add upstream https://github.com/umbraco/Umbraco-CMS.git
```
Then when you want to get the changes from the main repository:
```
git fetch upstream
git rebase upstream/v8/dev
```
In this command we're syncing with the `v8/dev` branch, but you can of course choose another one if needed.
(More info on how this works: [http://robots.thoughtbot.com/post/5133345960/keeping-a-git-fork-updated](http://robots.thoughtbot.com/post/5133345960/keeping-a-git-fork-updated))
_Looking for Umbraco version 8? [Click here](https://github.com/umbraco/Umbraco-CMS/blob/temp8/.github/V8_GETTING_STARTED.md) to go to the v8 branch_
# Contributing to Umbraco CMS
👍🎉 First off, thanks for taking the time to contribute! 🎉👍
The following is a set of guidelines for contributing to Umbraco CMS.
These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
Remember, we're a friendly bunch and are happy with whatever contribution you might provide. Below are guidelines for success that we've gathered over the years. If you choose to ignore them then we still love you 💖.
## Contributing code changes
This document gives you a quick overview on how to get started, we will link to in-depth documentation throughout if you need some more background info.
## Guidelines for contributions we welcome
Not all changes are wanted, so on occassion we might close a PR without merging it. We will give you feedback why we can't accept your changes and we'll be nice about it, thanking you for spending your valuable time.
We have [documented what we consider small and large changes](CONTRIBUTION_GUIDELINES.md). Make sure to talk to us before making large changes.
Remember, if an issue is in the `Up for grabs` list or you've asked for some feedback before you sent us a PR, your PR will not be closed as unwanted.
## How do I begin?
Great question! The short version goes like this:
* **Fork** - create a fork of [`Umbraco-CMS` on GitHub](https://github.com/umbraco/Umbraco-CMS)
![Fork the repository](img/forkrepository.png)
* **Clone** - when GitHub has created your fork, you can clone it in your favorite Git tool
![Clone the fork](img/clonefork.png)
* **Build** - build your fork of Umbraco locally as described in [building Umbraco from source code](BUILD.md)
* **Change** - make your changes, experiment, have fun, explore and learn, and don't be afraid. We welcome all contributions and will [happily give feedback](#questions)
* **Commit** - done? Yay! 🎉 It is recommended to create a new branch now and name it after the issue you're fixing, we usually follow the format: `temp-12345`. This means it's a temporary branch for the particular issue you're working on, in this case `12345`
* **Push** - great, now you can push the changes up to your fork on GitHub
* **Create pull request** - exciting! You're ready to show us your changes (or not quite ready, you just need some feedback to progress). GitHub has picked up on the new branch you've pushed and will offer to create a Pull Request. Click that green button and away you go.
![Create a pull request](img/createpullrequest.png)
### Further reading
At this point you might want to [read on about contributing in depth](CONTRIBUTING_DETAILED.md).
### Reviews
You've sent us your first contribution, congratulations! Now what?
The [pull request team](#the-pr-team) can now start reviewing your proposed changes and give you feedback on them. If it's not perfect, we'll either fix up what we need or we can request you to make some additional changes.
We have [a process in place which you can read all about](REVIEW_PROCESS.md). The very abbreviated version is:
- Your PR will get a reply within 48 hours
- An in-depth reply will be added within at most 2 weeks
- The PR will be either merged or rejected within at most 4 weeks
- Sometimes it is difficult to meet these timelines and we'll talk to you
## Styleguides
To be honest, we don't like rules very much. We trust you have the best of intentions and we encourage you to create working code. If it doesn't look perfect then we'll happily help clean it up.
That said, the Umbraco development team likes to follow the hints that ReSharper gives us (no problem if you don't have this installed) and we've added a `.editorconfig` file so that Visual Studio knows what to do with whitespace, line endings, etc.
## The PR team
The pull request team consists of a member of Umbraco HQ, [Sebastiaan](https://github.com/nul800sebastiaan), who gets assistance from the following community members
- [Anders Bjerner](https://github.com/abjerner)
- [Dave Woestenborghs](https://github.com/dawoe)
- [Emma Burstow](https://github.com/emmaburstow)
- [Poornima Nayar](https://github.com/poornimanayar)
These wonderful volunteers will provide you with a first reply to your PR, review and test out your changes and might ask more questions. After that they'll let Umbraco HQ know if everything seems okay.
## Questions?
You can get in touch with [the PR team](#the-pr-team) in multiple ways, we love open conversations and we are a friendly bunch. No question you have is stupid. Any questions you have usually helps out multiple people with the same question. Ask away:
- If there's an existing issue on the issue tracker then that's a good place to leave questions and discuss how to start or move forward
- Unsure where to start? Did something not work as expected? Try leaving a note in the ["Contributing to Umbraco"](https://our.umbraco.com/forum/contributing-to-umbraco-cms/) forum, the team monitors that one closely
## Code of Conduct
This project and everyone participating in it is governed by the [our Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [Sebastiaan Janssen - sj@umbraco.dk](mailto:sj@umbraco.dk).
## Contributing to Umbraco, in depth
There are other ways to contribute, and there's a few more things that you might be wondering about. We will answer the [most common questions and ways to contribute in our detailed documentation](CONTRIBUTING_DETAILED.md).
## Credits
This contribution guide borrows heavily from the excellent work on [the Atom contribution guide](https://github.com/atom/atom/blob/master/CONTRIBUTING.md). A big [#h5yr](http://h5yr.com/) to them!
+163
View File
@@ -0,0 +1,163 @@
# Contributing in detail
There's more than one way to contribute to Umbraco, there's some more suggestions below.
When contributing code to Umbraco there's plenty of things you'll want to know, skip down to [What should I know before I get started](#what-should-i-know-before-i-get-started) for the answers to your burning questions.
#### Table Of Contents
[How Can I Contribute?](#how-can-i-contribute)
* [Reporting Bugs](#reporting-bugs)
* [Suggesting Enhancements](#suggesting-enhancements)
* [Your First Code Contribution](#your-first-code-contribution)
* [Pull Requests](#pull-requests)
[Styleguides](#styleguides)
[What should I know before I get started?](#what-should-i-know-before-i-get-started)
* [Working with the source code](#working-with-the-source-code)
* [What branch should I target for my contributions?](#what-branch-should-i-target-for-my-contributions)
* [Building Umbraco from source code](#building-umbraco-from-source-code)
* [Keeping your Umbraco fork in sync with the main repository](#keeping-your-umbraco-fork-in-sync-with-the-main-repository)
## How Can I Contribute?
### Reporting Bugs
This section guides you through submitting a bug report for Umbraco CMS. Following these guidelines helps maintainers and the community understand your report 📝, reproduce the behavior 💻 💻, and find related reports 🔎.
Before creating bug reports, please check [this list](#before-submitting-a-bug-report) as you might find out that you don't need to create one. When you are creating a bug report, please [include as many details as possible](#how-do-i-submit-a-good-bug-report). Fill out [the required template](https://github.com/umbraco/Umbraco-CMS/issues/new/choose), the information it asks for helps us resolve issues faster.
> **Note:** If you find a **Closed** issue that seems like it is the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one.
##### Before Submitting A Bug Report
* Most importantly, check **if you can reproduce the problem** in the [latest version of Umbraco](https://our.umbraco.com/download/). We might have already fixed your particular problem.
* It also helps tremendously to check if the issue you're experiencing is present in **a clean install** of the Umbraco version you're currently using. Custom code can have side-effects that don't occur in a clean install.
* **Use the Google**. Whatever you're experiencing, Google it plus "Umbraco" - usually you can get some pretty good hints from the search results, including open issues and further troubleshooting hints.
* If you do find and existing issue has **and the issue is still open**, add a comment to the existing issue if you have additional information. If you have the same problem and no new info to add, just "star" the issue.
Explain the problem and include additional details to help maintainers reproduce the problem. The following is a long description which we've boiled down into a few very simple questions in the issue tracker when you create a new issue. We're listing the following hints to indicate that the most successful reports usually have a lot of this ground covered:
* **Use a clear and descriptive title** for the issue to identify the problem.
* **Describe the exact steps which reproduce the problem** in as many details as possible. For example, start by explaining which steps you took in the backoffice to get to a certain undesireable result, e.g. you created a document type, inherting 3 levels deep, added a certain datatype, tried to save it and you got an error.
* **Provide specific examples to demonstrate the steps**. If you wrote some code, try to provide a code sample as specific as possible to be able to reproduce the behavior.
* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior.
* **Explain which behavior you expected to see instead and why.**
Provide more context by answering these questions:
* **Can you reproduce the problem** when `debug="false"` in your `web.config` file?
* **Did the problem start happening recently** (e.g. after updating to a new version of Umbraco) or was this always a problem?
* **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens.
Include details about your configuration and environment:
* **Which version of Umbraco are you using?**
* **What is the environment you're using Umbraco in?** Is this a problem on your local machine or on a server. Tell us about your configuration: Windows version, IIS/IISExpress, database type, etc.
* **Which packages do you have installed?**
### Suggesting Enhancements
This section guides you through submitting an enhancement suggestion for Umbraco, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion 📝 and find related suggestions 🔎.
Most of the suggestions in the [reporting bugs](#reporting-bugs) section also count for suggesting enhancements.
Some additional hints that may be helpful:
* **Include screenshots and animated GIFs** which help you demonstrate the steps or point out the part of Umbraco which the suggestion is related to.
* **Explain why this enhancement would be useful to most Umbraco users** and isn't something that can or should be implemented as a [community package](https://our.umbraco.com/projects/).
### Your First Code Contribution
Unsure where to begin contributing to Umbraco? You can start by looking through [these `Up for grabs` and issues](https://issues.umbraco.org/issues?q=&project=U4&tagValue=upforgrabs&release=&issueType=&search=search) or on the [new issue tracker](https://github.com/umbraco/Umbraco-CMS/issues?q=is%3Aopen+is%3Aissue+label%3Acommunity%2Fup-for-grabs).
### Pull Requests
The most successful pull requests usually look a like this:
* Fill in the required template
* Include screenshots and animated GIFs in your pull request whenever possible.
* Unit tests, while optional are awesome, thank you!
* New code is commented with documentation from which [the reference documentation](https://our.umbraco.com/documentation/Reference/) is generated
Again, these are guidelines, not strict requirements.
## Making changes after the PR was opened
If you make the corrections we ask for in the same branch and push them to your fork again, the pull request automatically updates with the additional commit(s) so we can review it again. If all is well, we'll merge the code and your commits are forever part of Umbraco!
## Styleguides
To be honest, we don't like rules very much. We trust you have the best of intentions and we encourage you to create working code. If it doesn't look perfect then we'll happily help clean it up.
That said, the Umbraco development team likes to follow the hints that ReSharper gives us (no problem if you don't have this installed) and we've added a `.editorconfig` file so that Visual Studio knows what to do with whitespace, line endings, etc.
## What should I know before I get started?
### Working with the source code
Some parts of our source code is over 10 years old now. And when we say "old", we mean "mature" of course!
There's two big areas that you should know about:
1. The Umbraco backoffice is a extensible AngularJS app and requires you to run a `gulp dev` command while you're working with it, so changes are copied over to the appropriate directories and you can refresh your browser to view the results of your changes.
You may need to run the following commands to set up gulp properly:
```
npm cache clean
npm install -g bower
npm install -g gulp
npm install -g gulp-cli
npm install
gulp build
```
2. "The rest" is a C# based codebase, with some traces of our WebForms past but mostly ASP.NET MVC based these days. You can make changes, build them in Visual Studio, and hit `F5` to see the result.
To find the general areas of something you're looking to fix or improve, have a look at the following two parts of the API documentation.
* [The AngularJS based backoffice files](https://our.umbraco.com/apidocs/ui/#/api) (to be found in `src\Umbraco.Web.UI.Client\src`)
* [The rest](https://our.umbraco.com/apidocs/csharp/)
### What branch should I target for my contributions?
We like to use [Gitflow as much as possible](https://jeffkreeftmeijer.com/git-flow/), don't worry if you are not familiar with it. The most important thing you need to know is that when you fork the Umbraco repository, the default branch is set to something, usually `dev-v7`. Whatever the default is, that's where we'd like you to target your contributions.
![What branch do you want me to target?](img/defaultbranch.png)
### Building Umbraco from source code
In order to build the Umbraco source code locally, first make sure you have the following installed.
* Visual Studio 2017 v15.3+
* Node v10+ (Installed via `build.bat` script. If you already have it installed, make sure you're running at least v10)
* npm v6.4.1+ (Installed via `build.bat` script. If you already have it installed, make sure you're running at least v6.4.1)
The easiest way to get started is to run `build.bat` which will build both the backoffice (also known as "Belle") and the Umbraco core. You can then easily start debugging from Visual Studio, or if you need to debug Belle you can run `gulp dev` in `src\Umbraco.Web.UI.Client`. See [this page](BUILD.md) for more details.
Alternatively, you can open `src\umbraco.sln` in Visual Studio 2017 (version 15.3 or higher, [the community edition is free](https://www.visualstudio.com/thank-you-downloading-visual-studio/?sku=Community&rel=15) for you to use to contribute to Open Source projects). In Visual Studio, find the Task Runner Explorer (in the View menu under Other Windows) and run the build task under the gulpfile.
![Gulp build in Visual Studio](img/gulpbuild.png)
After this build completes, you should be able to hit `F5` in Visual Studio to build and run the project. A IISExpress webserver will start and the Umbraco installer will pop up in your browser, follow the directions there to get a working Umbraco install up and running.
### Keeping your Umbraco fork in sync with the main repository
We recommend you sync with our repository before you submit your pull request. That way, you can fix any potential merge conflicts and make our lives a little bit easier.
Also, if you've submitted a pull request three weeks ago and want to work on something new, you'll want to get the latest code to build against of course.
To sync your fork with this original one, you'll have to add the upstream url, you only have to do this once:
```
git remote add upstream https://github.com/umbraco/Umbraco-CMS.git
```
Then when you want to get the changes from the main repository:
```
git fetch upstream
git rebase upstream/dev-v7
```
In this command we're syncing with the `dev-v7` branch, but you can of course choose another one if needed.
(More info on how this works: [http://robots.thoughtbot.com/post/5133345960/keeping-a-git-fork-updated](http://robots.thoughtbot.com/post/5133345960/keeping-a-git-fork-updated))
+3 -5
View File
@@ -11,9 +11,9 @@ Were usually able to handle small PRs pretty quickly. A community volunteer w
### Up for grabs
Umbraco HQ will regularly mark newly created issues on the issue tracker with the `Up for grabs` tag. This means that the proposed changes are wanted in Umbraco but the HQ does not have the time to make them at this time. We encourage anyone to pick them up and help out.
Umbraco HQ will regularly mark newly created issues on the issue tracker with the `Up for grabs` tag. This means that the proposed changes are wanted in Umbraco but the HQ does not have the time to make them at this time. These issues are usually small enough to fit in the "Small PRs" category and we encourage anyone to pick them up and help out.
If you do start working on something, make sure leave a small comment on the issue saying something like: "I'm working on this". That way other people stumbling upon the issue know they don't need to pick it up, someone already has.
If you do start working on something, make sure to leave a small comment on the issue saying something like: "I'm working on this". That way other people stumbling upon the issue know they don't need to pick it up, someone already has.
## Large PRs
New features and large refactorings - can be recognized by seeing a large number of changes, plenty of new files, updates to package manager files (NuGets packages.config, NPMs packages.json, etc.).
@@ -24,9 +24,7 @@ Please make sure to describe your idea in an issue, it helps to put in mockup sc
If the change makes sense for HQ to include in Umbraco CMS we will leave you some feedback on how wed like to see it being implemented.
If a larger pull request is encouraged by Umbraco HQ, the process will be similar to what is described in the [small PRs process](#small-prs) above, we strive to feedback within 14 days. Finalizing and merging the PR might take longer though as it will likely need to be picked up by the development team to make sure everything is in order. Well keep you posted on the progress.
It is highly recommended that you speak to the HQ before making large, complex changes.
If a larger pull request is encouraged by Umbraco HQ, the process will be similar to what is described in the [small PRs process](#small-prs) above, well get feedback to you within 14 days. Finalizing and merging the PR might take longer though as it will likely need to be picked up by the development team to make sure everything is in order. Well keep you posted on the progress.
### Pull request or package?
+1 -1
View File
@@ -12,7 +12,7 @@ thoroughly. Then, proceed by filling out the rest of the details in the issue
template below. The more details you can give us, the easier it will be for us
to determine the cause of a problem.
See: https://github.com/umbraco/Umbraco-CMS/blob/v8/dev/.github/CONTRIBUTING.md
See: https://github.com/umbraco/Umbraco-CMS/blob/dev-v7/.github/CONTRIBUTING.md
-->
+2 -11
View File
@@ -2,20 +2,11 @@
- [ ] I have added steps to test this contribution in the description below
If there's an existing issue for this PR then this fixes <!-- link to the issue here! -->
If there's an existing issue for this PR then this fixes: <!-- link to the issue here! -->
### Description
<!--
A description of the changes proposed in the pull-request and how to test these changes.
<!-- A description of the changes proposed in the pull-request and how to test these changes -->
The most successful pull requests usually look a like this:
* Fill in this template with details: what did you do, why did you do it, how can we test the changes?
* Include screenshots and animated GIFs in your pull request whenever possible.
* Unit tests, while optional are awesome, thank you!
While these are guidelines and not strict requirements, they really help us evaluate your PR quicker.
-->
<!-- Thanks for contributing to Umbraco CMS! -->
+61 -40
View File
@@ -1,40 +1,61 @@
# [Umbraco CMS](https://umbraco.com) &middot; [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](../LICENSE.md) [![Build status](https://umbraco.visualstudio.com/Umbraco%20Cms/_apis/build/status/Cms%208%20Continuous?branchName=v8/dev)](https://umbraco.visualstudio.com/Umbraco%20Cms/_build?definitionId=75) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md) [![pullreminders](https://pullreminders.com/badge.svg)](https://pullreminders.com?ref=badge)
Umbraco is the friendliest, most flexible and fastest growing ASP.NET CMS, and used by more than 500,000 websites worldwide. Our mission is to help you deliver delightful digital experiences by making Umbraco friendly, simpler and social.
Learn more at [umbraco.com](https://umbraco.com)
<p align="center">
<img src="img/logo.png" alt="Umbraco Logo" />
</p>
See the official [Umbraco website](https://umbraco.com) for an introduction, core mission and values of the product and team behind it.
- [Getting Started](#getting-started)
- [Documentation](#documentation)
- [Community](#join-the-umbraco-community)
- [Contributing](#contributing)
Please also see our [Code of Conduct](CODE_OF_CONDUCT.md).
## Getting Started
[Umbraco Cloud](https://umbraco.com/cloud) is the easiest and fastest way to use Umbraco yet, with full support for all your custom .NET code and integrations. You're up and running in less than a minute, and your life will be made easier with automated upgrades and a built-in deployment engine. We offer a free 14-day trial, no credit card needed.
If you want to DIY, you can [download Umbraco]((https://our.umbraco.com/download)) either as a ZIP file or via NuGet. It's the same version of Umbraco CMS that powers Umbraco Cloud, but you'll need to find a place to host it yourself, and handling deployments and upgrades will be all up to you.
## Documentation
The documentation for Umbraco CMS can be found [on Our Umbraco](https://our.umbraco.com/documentation/). The source for the Umbraco docs is [open source as well](https://github.com/umbraco/UmbracoDocs) and we're happy to look at your documentation contributions.
## Join the Umbraco community
Our friendly community is available 24/7 at the community hub we call ["Our Umbraco"](https://our.umbraco.com/). Our Umbraco features forums for questions and answers, documentation, downloadable plugins for Umbraco, and a rich collection of community resources.
Besides "Our", we all support each other also via Twitter: [Umbraco HQ](https://twitter.com/umbraco), [Release Updates](https://twitter.com/umbracoproject), [#umbraco](https://twitter.com/hashtag/umbraco)
## Contributing
Umbraco is contribution-focused and community-driven. If you want to contribute back to the Umbraco source code, please check out our [guide to contributing](CONTRIBUTING.md).
_You are browsing the Umbraco v8 branch. Umbraco 8 is currently under development._
_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.
Umbraco CMS
===========
The friendliest, most flexible and fastest growing ASP.NET CMS, and used by more than 443,000 websites worldwide: [https://umbraco.com](https://umbraco.com)
[![ScreenShot](img/vimeo.png)](https://vimeo.com/172382998/)
## Umbraco CMS
Umbraco is a free open source Content Management System built on the ASP.NET platform. Our mission is to help you deliver delightful digital experiences by making Umbraco friendly, simpler and social.
## Watch an introduction video
[![ScreenShot](https://shop.umbraco.com/images/whatisumbraco.png)](https://umbraco.tv/videos/umbraco-v7/content-editor/basics/introduction/cms-explanation/)
## Umbraco - The Friendly CMS
For the first time on the Microsoft platform, there is a free user- and developer-friendly CMS that makes it quick and easy to create websites - and a breeze to build complex web applications. Umbraco has award-winning integration capabilities and supports ASP.NET MVC or Web Forms, including User and Custom Controls, right out of the box.
Umbraco is not only loved by developers, but is a content editor's dream. Enjoy intuitive editing tools, media management, responsive views, and approval workflows to send your content live.
Used by more than 443,000 active websites including Carlsberg, Segway, Amazon and Heinz and **The Official ASP.NET and IIS.NET website from Microsoft** ([https://asp.net](https://asp.net) / [https://iis.net](https://iis.net)), you can be sure that the technology is proven, stable and scalable. Backed by the team at Umbraco HQ, and supported by a dedicated community of over 220,000 craftspeople globally, you can trust that Umbraco is a safe choice and is here to stay.
To view more examples, please visit [https://umbraco.com/case-studies-testimonials/](https://umbraco.com/case-studies-testimonials/)
## Why Open Source?
As an Open Source platform, Umbraco is more than just a CMS. We are transparent with our roadmap for future versions, our incremental sprint planning notes are publicly accessible, and community contributions and packages are available for all to use.
## Trying out Umbraco CMS
[Umbraco Cloud](https://umbraco.com/cloud) is the easiest and fastest way to use Umbraco yet, with full support for all your custom .NET code and integrations. You're up and running in less than a minute, and your life will be made easier with automated upgrades and a built-in deployment engine. We offer a free 14-day trial, no credit card needed.
If you want to DIY, you can [download Umbraco](https://our.umbraco.com/download) either as a ZIP file or via NuGet. It's the same version of Umbraco CMS that powers Umbraco Cloud, but you'll need to find a place to host it yourself, and handling deployments and upgrades will be all up to you.
## Community
Our friendly community is available 24/7 at the community hub we call ["Our Umbraco"](https://our.umbraco.com). Our Umbraco features forums for questions and answers, documentation, downloadable plugins for Umbraco, and a rich collection of community resources.
## Contribute to Umbraco
Umbraco is contribution-focused and community-driven. If you want to contribute back to Umbraco, please check out our [guide to contributing](CONTRIBUTING.md).
## 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).
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).
+37
View File
@@ -0,0 +1,37 @@
## 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://github.com/umbraco/Umbraco-CMS/labels/release%2F8.0.0). 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.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

+159 -164
View File
@@ -1,164 +1,159 @@
#
# Umbraco Cms Git Ignore
#
# common files
*.obj
*.pdb
*.user
*.aps
*.pch
*.vspscc
*.orig
*.suo
*.vs10x
*.ndproj
*.ignorer.*
# common directories
.DS_Store
._.DS_Store
.vs/
/local/
# build directories
[Bb]in/
[Db]ebug*/
[Rr]elease*/
obj/
# tools
_ReSharper*/
_NCrunch_*/
*.ncrunchsolution
*.ncrunchsolution.user
*.ncrunchproject
*.crunchsolution.cache
tools/NDepend/
[Tt]est[Rr]esult*
[Bb]uild[Ll]og.*
*.[Pp]ublish.xml
[sS]ource
[sS]andbox
umbraco.config
App_Data/TEMP/*
[Uu]mbraco/[Pp]resentation/[Uu]mbraco/[Pp]lugins/*
[Uu]mbraco/[Pp]resentation/[Uu]ser[Cc]ontrols/*
[Uu]mbraco/[Pp]resentation/[Ss]cripts/*
[Uu]mbraco/[Pp]resentation/[Ff]onts/*
[Uu]mbraco/[Pp]resentation/[Cc]ss/*
src/Umbraco.Web.UI/[Cc]ss/*
src/Umbraco.Web.UI/App_Code/*
src/Umbraco.Web.UI/App_Data/*
src/Umbraco.Tests/App_Data/*
src/Umbraco.Web.UI/[Mm]edia/*
src/Umbraco.Web.UI/[Mm]aster[Pp]ages/*
src/Umbraco.Web.UI/[Mm]acro[Ss]cripts/*
!src/Umbraco.Web.UI/[Mm]acro[Ss]cripts/[Ww]eb.[Cc]onfig
src/Umbraco.Web.UI/[Xx]slt/*
src/Umbraco.Web.UI/[Ii]mages/*
src/Umbraco.Web.UI/[Ss]cripts/*
src/Umbraco.Web.UI/Web.*.config.transformed
umbraco/presentation/umbraco/plugins/uComponents/uComponentsInstaller.ascx
umbraco/presentation/packages/uComponents/MultiNodePicker/CustomTreeService.asmx
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/[Cc]onfig/serilog.config
src/Umbraco.Web.UI/[Cc]onfig/serilog.user.config
src/Umbraco.Tests/config/404handlers.config
src/Umbraco.Web.UI/[Vv]iews/*.cshtml
src/Umbraco.Web.UI/[Vv]iews/*.vbhtml
src/Umbraco.Tests/[Cc]onfig/umbracoSettings.config
src/Umbraco.Web.UI/[Vv]iews/*
src/packages/
src/packages/repositories.config
src/Umbraco.Web.UI/[Ww]eb.config
*.transformed
node_modules
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/[Vv]iews/
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.js
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.css
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.html
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
src/Umbraco.Web.UI.Client/vwd.webinfo
src/Umbraco.Web.UI/App_Plugins/*
src/*.psess
src/*.vspx
NDependOut/*
QueryResult.htm
src/Umbraco.Web.UI/[Cc]onfig/appSettings.config
src/Umbraco.Web.UI/[Cc]onfig/connectionStrings.config
src/Umbraco.Web.UI/[Uu]mbraco/plugins/*
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
#Ignore Rule for output of generated documentation files from Grunt docserve
src/Umbraco.Web.UI.Client/docs/api
src/*.boltdata/
src/umbraco.sln.ide/*
src/.vs/
src/Umbraco.Web.UI/[Jj]s/*
src/Umbraco.Tests/[Mm]edia
tools/docfx/*
apidocs/_site/*
src/*/project.lock.json
src/.idea/*
apidocs/api/*
build/docs.zip
build/ui-docs.zip
build/csharp-docs.zip
src/packages/
src/PrecompiledWeb/*
# build
build.out/
build.tmp/
build/hooks/
build/temp/
# eof
/src/Umbraco.Web.UI.Client/TESTS-*.xml
/src/ApiDocs/api/*
#
# 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/
[Db]ebug*/
[Rr]elease*/
obj/
# tools
_ReSharper*/
_NCrunch_*/
*.ncrunchsolution
*.ncrunchsolution.user
*.ncrunchproject
*.crunchsolution.cache
tools/NDepend/
[Tt]est[Rr]esult*
[Bb]uild[Ll]og.*
*.[Pp]ublish.xml
[sS]ource
[sS]andbox
umbraco.config
App_Data/TEMP/*
[Uu]mbraco/[Pp]resentation/[Uu]mbraco/[Pp]lugins/*
[Uu]mbraco/[Pp]resentation/[Uu]ser[Cc]ontrols/*
[Uu]mbraco/[Pp]resentation/[Ss]cripts/*
[Uu]mbraco/[Pp]resentation/[Ff]onts/*
[Uu]mbraco/[Pp]resentation/[Cc]ss/*
src/Umbraco.Web.UI/[Cc]ss/*
src/Umbraco.Web.UI/App_Code/*
src/Umbraco.Web.UI/App_Data/*
src/Umbraco.Tests/App_Data/*
src/Umbraco.Web.UI/[Mm]edia/*
src/Umbraco.Web.UI/[Mm]aster[Pp]ages/*
src/Umbraco.Web.UI/[Mm]acro[Ss]cripts/*
!src/Umbraco.Web.UI/[Mm]acro[Ss]cripts/[Ww]eb.[Cc]onfig
src/Umbraco.Web.UI/[Xx]slt/*
src/Umbraco.Web.UI/[Ii]mages/*
src/Umbraco.Web.UI/[Ss]cripts/*
src/Umbraco.Web.UI/Web.*.config.transformed
umbraco/presentation/umbraco/plugins/uComponents/uComponentsInstaller.ascx
umbraco/presentation/packages/uComponents/MultiNodePicker/CustomTreeService.asmx
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.Tests/config/404handlers.config
src/Umbraco.Web.UI/[Vv]iews/*.cshtml
src/Umbraco.Web.UI/[Vv]iews/*.vbhtml
src/Umbraco.Tests/[Cc]onfig/umbracoSettings.config
src/Umbraco.Web.UI/[Vv]iews/*
src/packages/
src/packages/repositories.config
src/Umbraco.Web.UI/[Ww]eb.config
*.transformed
node_modules
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/[Vv]iews/
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.js
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.css
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.html
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
src/Umbraco.Web.UI.Client/vwd.webinfo
src/Umbraco.Web.UI/App_Plugins/*
src/*.psess
src/*.vspx
NDependOut/*
QueryResult.htm
src/Umbraco.Web.UI/[Cc]onfig/appSettings.config
src/Umbraco.Web.UI/[Cc]onfig/connectionStrings.config
src/Umbraco.Web.UI/[Uu]mbraco/plugins/*
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
#Ignore Rule for output of generated documentation files from Grunt docserve
src/Umbraco.Web.UI.Client/docs/api
src/*.boltdata/
src/umbraco.sln.ide/*
src/.vs/
src/Umbraco.Web.UI/[Jj]s/*
src/Umbraco.Tests/[Mm]edia
tools/docfx/*
apidocs/_site/*
src/*/project.lock.json
src/.idea/*
apidocs/api/*
build/docs.zip
build/ui-docs.zip
build/csharp-docs.zip
src/packages/
src/PrecompiledWeb/*
# build
build.out/
build.tmp/
build/hooks/
build/temp/
# eof
+20 -20
View File
@@ -3,57 +3,57 @@
[string]$Directory
)
$workingDirectory = $Directory
CD "$($workingDirectory)"
CD $workingDirectory
# Clone repo
$fullGitUrl = "https://$($env:GIT_URL)/$($env:GIT_REPOSITORYNAME).git"
git clone $($fullGitUrl) $($env:GIT_REPOSITORYNAME) 2>&1 | % { $_.ToString() }
$fullGitUrl = "https://$env:GIT_URL/$env:GIT_REPOSITORYNAME.git"
git clone $fullGitUrl 2>&1 | % { $_.ToString() }
# Remove everything so that unzipping the release later will update everything
# Don't remove the readme file nor the git directory
Write-Host "Cleaning up git directory before adding new version"
Remove-Item -Recurse "$($workingDirectory)\$($env:GIT_REPOSITORYNAME)\*" -Exclude README.md,.git
Remove-Item -Recurse $workingDirectory\$env:GIT_REPOSITORYNAME\* -Exclude README.md,.git
# Find release zip
$zipsDir = "$($workingDirectory)\$($env:BUILD_DEFINITIONNAME)\zips"
$zipsDir = "$workingDirectory\$env:BUILD_DEFINITIONNAME\zips"
$pattern = "UmbracoCms.([0-9]{1,2}.[0-9]{1,3}.[0-9]{1,3}).zip"
Write-Host "Searching for Umbraco release files in $($zipsDir) for a file with pattern $($pattern)"
$file = (Get-ChildItem "$($zipsDir)" | Where-Object { $_.Name -match "$($pattern)" })
Write-Host "Searching for Umbraco release files in $workingDirectory\$zipsDir for a file with pattern $pattern"
$file = (Get-ChildItem $zipsDir | Where-Object { $_.Name -match "$pattern" })
if($file)
{
# Get release name
$version = [regex]::Match($($file.Name), $($pattern)).captures.groups[1].value
$releaseName = "Umbraco $($version)"
Write-Host "Found $($releaseName)"
$version = [regex]::Match($file.Name, $pattern).captures.groups[1].value
$releaseName = "Umbraco $version"
Write-Host "Found $releaseName"
# Unzip into repository to update release
Add-Type -AssemblyName System.IO.Compression.FileSystem
Write-Host "Unzipping $($file.FullName) to $($workingDirectory)\$($env:GIT_REPOSITORYNAME)"
[System.IO.Compression.ZipFile]::ExtractToDirectory("$($file.FullName)", "$($workingDirectory)\$($env:GIT_REPOSITORYNAME)")
Write-Host "Unzipping $($file.FullName) to $workingDirectory\$env:GIT_REPOSITORYNAME"
[System.IO.Compression.ZipFile]::ExtractToDirectory("$($file.FullName)", "$workingDirectory\$env:GIT_REPOSITORYNAME")
# Telling git who we are
git config --global user.email "coffee@umbraco.com" 2>&1 | % { $_.ToString() }
git config --global user.name "Umbraco HQ" 2>&1 | % { $_.ToString() }
# Commit
CD "$($workingDirectory)\$($env:GIT_REPOSITORYNAME)"
Write-Host "Committing Umbraco $($version) Release from Build Output"
CD $env:GIT_REPOSITORYNAME
Write-Host "Committing Umbraco $version Release from Build Output"
git add . 2>&1 | % { $_.ToString() }
git commit -m " Release $($releaseName) from Build Output" 2>&1 | % { $_.ToString() }
git commit -m " Release $releaseName from Build Output" 2>&1 | % { $_.ToString() }
# Tag the release
git tag -a "v$($version)" -m "v$($version)"
git tag -a "v$version" -m "v$version"
# Push release to master
$fullGitAuthUrl = "https://$($env:GIT_USERNAME):$($GitHubPersonalAccessToken)@$($env:GIT_URL)/$($env:GIT_REPOSITORYNAME).git"
git push $($fullGitAuthUrl) 2>&1 | % { $_.ToString() }
$fullGitAuthUrl = "https://$($env:GIT_USERNAME):$GitHubPersonalAccessToken@$env:GIT_URL/$env:GIT_REPOSITORYNAME.git"
git push $fullGitAuthUrl 2>&1 | % { $_.ToString() }
#Push tag to master
git push $($fullGitAuthUrl) --tags 2>&1 | % { $_.ToString() }
git push $fullGitAuthUrl --tags 2>&1 | % { $_.ToString() }
}
else
{
Write-Error "Umbraco release file not found, searched in $($workingDirectory)\$($zipsDir) for a file with pattern $($pattern) - canceling"
Write-Error "Umbraco release file not found, searched in $workingDirectory\$zipsDir for a file with pattern $pattern - cancelling"
}
+30 -35
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata minClientVersion="4.1.0">
<metadata minClientVersion="3.4.4">
<id>UmbracoCms.Core</id>
<version>8.0.0</version>
<title>Umbraco Cms Core Binaries</title>
@@ -8,44 +8,38 @@
<owners>Umbraco HQ</owners>
<licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
<projectUrl>http://umbraco.com/</projectUrl>
<iconUrl>https://umbraco.com/dist/nuget/logo-small.png</iconUrl>
<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>
<repository type="git" url="https://github.com/umbraco/umbraco-cms" />
<dependencies>
<group targetFramework="net472">
<!--
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="LightInject" version="[5.4.0,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.7,5.999999)" />
<dependency id="Microsoft.Owin" version="[4.0.1,4.999999)" />
<dependency id="MiniProfiler" version="[4.0.138,4.999999)" />
<dependency id="Newtonsoft.Json" version="[12.0.1,12.999999)" />
<dependency id="Semver" version="[2.0.4,2.999999)" />
<dependency id="Serilog" version="[2.8.0,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.Formatting.Compact.Reader" version="[1.0.3,1.999999)" />
<dependency id="Serilog.Settings.AppSettings" version="[2.2.2,2.999999)" />
<dependency id="Serilog.Sinks.File" version="[4.0.0,4.999999)" />
<dependency id="Serilog.Sinks.Map" version="[1.0.0,1.999999)" />
<dependency id="Serilog.Sinks.Async" version="[1.3.0,1.999999)" />
<dependency id="Umbraco.SqlServerCE" version="[4.0.0.1,4.999999)" />
<dependency id="NPoco" version="[3.9.4,3.999999)" />
</group>
<!--
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.2.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>
@@ -53,9 +47,10 @@
<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\net472\Umbraco.Core.xml" />
<file src="$BuildTmp$\WebApp\bin\Umbraco.Core.xml" target="lib\Umbraco.Core.xml" />
<!-- symbols -->
<file src="$BuildTmp$\bin\Umbraco.Core.pdb" target="lib\net472\Umbraco.Core.pdb" />
<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>
+34 -38
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata minClientVersion="4.1.0">
<metadata minClientVersion="3.4.4">
<id>UmbracoCms.Web</id>
<version>8.0.0</version>
<title>Umbraco Cms Core Binaries</title>
@@ -8,43 +8,37 @@
<owners>Umbraco HQ</owners>
<licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
<projectUrl>http://umbraco.com/</projectUrl>
<iconUrl>https://umbraco.com/dist/nuget/logo-small.png</iconUrl>
<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>
<repository type="git" url="https://github.com/umbraco/umbraco-cms" />
<dependencies>
<group targetFramework="net472">
<!--
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="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,1.999999)" />
<dependency id="HtmlAgilityPack" version="[1.8.14,1.999999)" />
<dependency id="ImageProcessor" version="[2.7.0.100,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.7,5.999999)" />
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.4.0,2.999999)" />
<dependency id="Microsoft.AspNet.WebApi" version="[5.2.7,5.999999)" />
<dependency id="Microsoft.Owin.Host.SystemWeb" version="[4.0.1,4.999999)" />
<dependency id="Microsoft.Owin.Security.Cookies" version="[4.0.1,4.999999)" />
<dependency id="Microsoft.Owin.Security.OAuth" version="[4.0.1,4.999999)" />
<dependency id="System.Threading.Tasks.Dataflow" version="[4.9.0,4.999999)" />
</group>
<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-beta067,1.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>
@@ -54,12 +48,14 @@
<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\net472\Umbraco.Web.xml" />
<file src="$BuildTmp$\WebApp\bin\Umbraco.Web.UI.xml" target="lib\net472\Umbraco.Web.UI.xml" />
<file src="$BuildTmp$\WebApp\bin\Umbraco.Examine.xml" target="lib\net472\Umbraco.Examine.xml" />
<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\net472\Umbraco.Web.pdb" />
<file src="$BuildTmp$\bin\Umbraco.Examine.pdb" target="lib\net472\Umbraco.Examine.pdb" />
<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>
+19 -24
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata minClientVersion="4.1.0">
<metadata minClientVersion="3.4.4">
<id>UmbracoCms</id>
<version>8.0.0</version>
<title>Umbraco Cms</title>
@@ -8,39 +8,32 @@
<owners>Umbraco HQ</owners>
<licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
<projectUrl>http://umbraco.com/</projectUrl>
<iconUrl>https://umbraco.com/dist/nuget/logo-small.png</iconUrl>
<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>
<repository type="git" url="https://github.com/umbraco/umbraco-cms" />
<dependencies>
<group targetFramework="net472">
<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.4.0, 2.999999)" />
<dependency id="Umbraco.ModelsBuilder.Ui" version="[8.1.0]" />
<dependency id="ImageProcessor.Web" version="[4.10.0.100,4.999999)" />
<dependency id="ImageProcessor.Web.Config" version="[2.5.0.100,2.999999)" />
<dependency id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="[2.0.1,2.999999)" />
<dependency id="Microsoft.Net.Compilers" version="[2.10.0,2.999999)" />
</group>
<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.29]" />
<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.1,2.999999)" />
<dependency id="Microsoft.Net.Compilers" version="[2.10.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" />
@@ -59,10 +52,12 @@
<!-- 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\serilog.config.install.xdt" target="Content\config\serilog.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?! -->
<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" />
@@ -0,0 +1,95 @@
<?xml version="1.0"?>
<dashBoard xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<section alias="StartupSettingsDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
<tab caption="Get Started" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
<tab caption="Welcome" xdt:Locator="Match(caption)" xdt:Transform="InsertIfMissing">
<control panelCaption="" xdt:Transform="InsertIfMissing">
views/dashboard/settings/settingsdashboardintro.html
</control>
</tab>
</section>
<section alias="StartupFormsDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
<areas>
<area>forms</area>
</areas>
<tab caption="Install Umbraco Forms">
<control panelCaption="">
views/dashboard/forms/formsdashboardintro.html
</control>
</tab>
</section>
<section alias="StartupDeveloperDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
<areas xdt:Transform="InsertIfMissing">
<area xdt:Transform="InsertIfMissing">developer</area>
</areas>
</section>
<section alias="StartupDeveloperDashboardSection">
<tab caption="Get Started" xdt:Locator="Match(caption)" xdt:Transform="Replace">
<control panelCaption="">
views/dashboard/developer/developerdashboardvideos.html
</control>
</tab>
<tab caption="Examine Management" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
</section>
<section alias="StartupDeveloperDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
<tab caption="Examine Management" xdt:Locator="Match(caption)" xdt:Transform="InsertIfMissing">
<control>
views/dashboard/developer/examinemanagement.html
</control>
</tab>
</section>
<section alias="StartupMediaDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
<tab caption="Get Started" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
<tab caption="Content" xdt:Locator="Match(caption)" xdt:Transform="InsertIfMissing" />
<tab caption="Content" xdt:Locator="Match(caption)" xdt:Transform="Replace">
<control panelCaption="">
views/dashboard/media/mediafolderbrowser.html
</control>
</tab>
</section>
<section alias="StartupMemberDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
<tab caption="Get Started" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
<tab caption="Get Started" xdt:Transform="Insert">
<control panelCaption="">
views/dashboard/members/membersdashboardvideos.html
</control>
</tab>
</section>
<section alias="uGoLiveDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="Remove" />
<section alias="ExamineManagement" xdt:Locator="Match(alias)" xdt:Transform="Remove" />
<section alias="StartupDashboardSection">
<tab caption="Change Password" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
<tab caption="Last Edits" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
</section>
<section alias="RedirectUrlManagement" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
<areas>
<area>content</area>
</areas>
<tab caption="Redirect URL Management">
<control>
views/dashboard/developer/redirecturls.html
</control>
</tab>
</section>
<section alias="UmbracoHealthCheck" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
<areas>
<area>developer</area>
</areas>
<tab caption="Health Check">
<control>
views/dashboard/developer/healthcheck.html
</control>
</tab>
</section>
</dashBoard>
+7 -8
View File
@@ -1,13 +1,12 @@
888
888
888 888 88888b.d88b. 88888b. 888d888 8888b. .d8888b .d88b.
888 888 888 "888 "88b 888 "88b 888P" "88b d88P" d88""88b
888 888 888 888 888 888 888 888 .d888888 888 888 888
Y88 88Y 888 888 888 888 d88P 888 888 888 Y88b. Y88..88P
"Y888P" 888 888 888 88888P" 888 "Y888888 "Y8888P "Y88P"
_ _ __ __ ____ _____ _____ ____
| | | | \/ | _ \| __ \ /\ / ____/ __ \
| | | | \ / | |_) | |__) | / \ | | | | | |
| | | | |\/| | _ <| _ / / /\ \| | | | | |
| |__| | | | | |_) | | \ \ / ____ | |___| |__| |
\____/|_| |_|____/|_| \_/_/ \_\_____\____/
------------------------------------------------------------------
----------------------------------------------------
Don't forget to build!
+7 -8
View File
@@ -1,13 +1,12 @@
888
888
888 888 88888b.d88b. 88888b. 888d888 8888b. .d8888b .d88b.
888 888 888 "888 "88b 888 "88b 888P" "88b d88P" d88""88b
888 888 888 888 888 888 888 888 .d888888 888 888 888
Y88 88Y 888 888 888 888 d88P 888 888 888 Y88b. Y88..88P
"Y888P" 888 888 888 88888P" 888 "Y888888 "Y8888P "Y88P"
_ _ __ __ ____ _____ _____ ____
| | | | \/ | _ \| __ \ /\ / ____/ __ \
| | | | \ / | |_) | |__) | / \ | | | | | |
| | | | |\/| | _ <| _ / / /\ \| | | | | |
| |__| | | | | |_) | | \ \ / ____ | |___| |__| |
\____/|_| |_|____/|_| \_/_/ \_\_____\____/
------------------------------------------------------------------
----------------------------------------------------
Don't forget to build!
@@ -8,7 +8,7 @@
</configSections>
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" xdt:Locator="Match(factoryType)" xdt:Transform="SetAttributes(factoryType)" />
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" xdt:Locator="Match(factoryType)" xdt:Transform="SetAttributes(factoryType)" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="Umbraco.Web.PublishedContentModels" xdt:Transform="InsertIfMissing" />
@@ -18,13 +18,13 @@
<system.web>
<pages
pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
xdt:Locator="Match(pageParserFilterType,pageBaseType,userControlBaseType)"
xdt:Transform="SetAttributes(pageParserFilterType,pageBaseType,userControlBaseType)">
<controls>
<add assembly="System.Web.Mvc, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" xdt:Locator="Match(namespace)" xdt:Transform="SetAttributes(assembly)" />
<add assembly="System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" xdt:Locator="Match(namespace)" xdt:Transform="SetAttributes(assembly)" />
</controls>
</pages>
</system.web>
+59 -50
View File
@@ -2,11 +2,17 @@
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<configSections xdt:Transform="InsertIfMissing" />
<configSections>
<sectionGroup name="applicationSettings" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<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)" />
<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="dashBoard" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<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>
@@ -16,18 +22,26 @@
<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" />
<dashBoard configSource="config\Dashboard.config" xdt:Locator="Match(configSource)" xdt:Transform="Remove" />
<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" />
<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)" />
@@ -35,9 +49,14 @@
<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.7.2" xdt:Locator="Condition(count(@targetFramework) != 1)" xdt:Transform="SetAttributes(targetFramework)" />
<httpRuntime targetFramework="4.5" xdt:Locator="Condition(count(@targetFramework) != 1)" xdt:Transform="SetAttributes(targetFramework)" />
<membership defaultProvider="DefaultMembershipProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove" />
<roleManager defaultProvider="DefaultRoleProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove"/>
@@ -126,55 +145,45 @@
<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" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.ValueTuple')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Net.Http.Formatting')" 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.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin.Security.Cookies" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin.Security.OAuth" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.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.3.0" newVersion="1.2.3.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.7.0" newVersion="5.2.7.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.7.0" newVersion="5.2.7.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="0.0.0.0-5.2.7.0" newVersion="5.2.7.0" />
</dependentAssembly>
</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" />
+94
View File
@@ -0,0 +1,94 @@
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}"
$umbracoBinFolder = Join-Path $projectPath "bin"
Write-Host "umbracoBinFolder:" "${umbracoBinFolder}"
# 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
# After backing up, remove all umbraco dlls from bin folder in case dll files are included in the VS project
# See: http://issues.umbraco.org/issue/U4-4930
if(Test-Path $umbracoBinFolder) {
$umbracoBinBackupPath = Join-Path $backupPath "bin"
New-Item -ItemType Directory -Force -Path $umbracoBinBackupPath
robocopy $umbracoBinFolder $umbracoBinBackupPath /e /LOG:$copyLogsPath\UmbracoBinBackup.log
# Delete files Umbraco ships with
if(Test-Path $umbracoBinFolder\Microsoft.ApplicationBlocks.Data.dll) { Remove-Item $umbracoBinFolder\Microsoft.ApplicationBlocks.Data.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\Umbraco.Core.dll) { Remove-Item $umbracoBinFolder\Umbraco.Core.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.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 }
# Delete files Umbraco depends upon
$amd64Folder = Join-Path $umbracoBinFolder "amd64"
if(Test-Path $amd64Folder) { Remove-Item $amd64Folder -Force -Recurse -Confirm:$false }
$x86Folder = Join-Path $umbracoBinFolder "x86"
if(Test-Path $x86Folder) { Remove-Item $x86Folder -Force -Recurse -Confirm:$false }
if(Test-Path $umbracoBinFolder\AutoMapper.dll) { Remove-Item $umbracoBinFolder\AutoMapper.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\AutoMapper.Net4.dll) { Remove-Item $umbracoBinFolder\AutoMapper.Net4.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\ClientDependency.Core.dll) { Remove-Item $umbracoBinFolder\ClientDependency.Core.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\ClientDependency.Core.Mvc.dll) { Remove-Item $umbracoBinFolder\ClientDependency.Core.Mvc.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\CookComputing.XmlRpcV2.dll) { Remove-Item $umbracoBinFolder\CookComputing.XmlRpcV2.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Examine.dll) { Remove-Item $umbracoBinFolder\Examine.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\HtmlAgilityPack.dll) { Remove-Item $umbracoBinFolder\HtmlAgilityPack.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\ICSharpCode.SharpZipLib.dll) { Remove-Item $umbracoBinFolder\ICSharpCode.SharpZipLib.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\ImageProcessor.dll) { Remove-Item $umbracoBinFolder\ImageProcessor.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\ImageProcessor.Web.dll) { Remove-Item $umbracoBinFolder\ImageProcessor.Web.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Lucene.Net.dll) { Remove-Item $umbracoBinFolder\Lucene.Net.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.IO.RecyclableMemoryStream.dll) { Remove-Item $umbracoBinFolder\Microsoft.IO.RecyclableMemoryStream.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.AspNet.Identity.Core.dll) { Remove-Item $umbracoBinFolder\Microsoft.AspNet.Identity.Core.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.AspNet.Identity.Owin.dll) { Remove-Item $umbracoBinFolder\Microsoft.AspNet.Identity.Owin.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.CodeAnalysis.CSharp.dll) { Remove-Item $umbracoBinFolder\Microsoft.CodeAnalysis.CSharp.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.CodeAnalysis.dll) { Remove-Item $umbracoBinFolder\Microsoft.CodeAnalysis.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.Owin.dll) { Remove-Item $umbracoBinFolder\Microsoft.Owin.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.Owin.Host.SystemWeb.dll) { Remove-Item $umbracoBinFolder\Microsoft.Owin.Host.SystemWeb.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.Owin.Security.dll) { Remove-Item $umbracoBinFolder\Microsoft.Owin.Security.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.Owin.Security.Cookies.dll) { Remove-Item $umbracoBinFolder\Microsoft.Owin.Security.Cookies.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.Owin.Security.OAuth.dll) { Remove-Item $umbracoBinFolder\Microsoft.Owin.Security.OAuth.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.Web.Infrastructure.dll) { Remove-Item $umbracoBinFolder\Microsoft.Web.Infrastructure.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.Web.Helpers.dll) { Remove-Item $umbracoBinFolder\Microsoft.Web.Helpers.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.Web.Mvc.FixedDisplayModes.dll) { Remove-Item $umbracoBinFolder\Microsoft.Web.Mvc.FixedDisplayModes.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\MiniProfiler.dll) { Remove-Item $umbracoBinFolder\MiniProfiler.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\MySql.Data.dll) { Remove-Item $umbracoBinFolder\MySql.Data.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Newtonsoft.Json.dll) { Remove-Item $umbracoBinFolder\Newtonsoft.Json.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Owin.dll) { Remove-Item $umbracoBinFolder\Owin.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Semver.dll) { Remove-Item $umbracoBinFolder\Semver.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Collections.Immutable.dll) { Remove-Item $umbracoBinFolder\System.Collections.Immutable.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Reflection.Metadata.dll) { Remove-Item $umbracoBinFolder\System.Reflection.Metadata.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Net.Http.Extensions.dll) { Remove-Item $umbracoBinFolder\System.Net.Http.Extensions.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Net.Http.Formatting.dll) { Remove-Item $umbracoBinFolder\System.Net.Http.Formatting.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Net.Http.Primitives.dll) { Remove-Item $umbracoBinFolder\System.Net.Http.Primitives.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.Helpers.dll) { Remove-Item $umbracoBinFolder\System.Web.Helpers.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.Http.dll) { Remove-Item $umbracoBinFolder\System.Web.Http.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.Http.WebHost.dll) { Remove-Item $umbracoBinFolder\System.Web.Http.WebHost.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.Mvc.dll) { Remove-Item $umbracoBinFolder\System.Web.Mvc.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.Razor.dll) { Remove-Item $umbracoBinFolder\System.Web.Razor.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.WebPages.dll) { Remove-Item $umbracoBinFolder\System.Web.WebPages.dll -Force -Confirm:$false }
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 }
}
}
+61 -22
View File
@@ -11,27 +11,38 @@ if ($project) {
# 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
New-Item -ItemType Directory -Force -Path $umbracoFolder
$umbracoFolderSource = Join-Path $installPath "UmbracoFiles\Umbraco"
Write-Host "copying files to $umbracoFolder ..."
# see https://support.microsoft.com/en-us/help/954404/return-codes-that-are-used-by-the-robocopy-utility-in-windows-server-2
robocopy $umbracoFolderSource $umbracoFolder /is /it /e
if (($lastexitcode -eq 1) -or ($lastexitcode -eq 3) -or ($lastexitcode -eq 5) -or ($lastexitcode -eq 7))
{
write-host "Copy succeeded!"
}
else
{
write-host "Copy failed with exit code:" $lastexitcode
}
$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"
@@ -43,7 +54,7 @@ if ($project) {
[xml]$config = Get-Content $destinationWebConfig
$config.configuration.appSettings.ChildNodes | ForEach-Object {
if($_.key -eq "Umbraco.Core.ConfigurationStatus")
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
@@ -51,11 +62,7 @@ if ($project) {
}
}
}
Catch
{
Write-Host "An error occurred:"
Write-Host $_
}
Catch { }
}
if($copyWebconfig -eq $true)
@@ -70,6 +77,10 @@ if ($project) {
$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
@@ -89,10 +100,38 @@ if ($project) {
}
Catch
{
# Not a big problem if this fails, let it go
# Write-Host "An error occurred:"
# Write-Host $_
# 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
@@ -0,0 +1,20 @@
<?xml version="1.0"?>
<log4net xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<appender name="AsynchronousLog4NetAppender" xdt:Transform="Remove" xdt:Locator="Match(name)" />
<appender name="AsynchronousLog4NetAppender" type="Umbraco.Core.Logging.ParallelForwardingAppender,Umbraco.Core" xdt:Transform="Insert" >
<appender-ref ref="rollingFile" />
</appender>
<appender name="rollingFile" type="log4net.Appender.RollingFileAppender" xdt:Transform="InsertIfMissing">
<file value="App_Data\Logs\UmbracoTraceLog.txt" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<appendToFile value="true" />
<rollingStyle value="Date" />
<maximumFileSize value="5MB" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value=" %date [P%property{processId}/D%property{appDomainId}/T%thread] %-5level %logger - %message%newline" />
</layout>
<encoding value="utf-8" />
</appender>
</log4net>
@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">>
<appSettings>
<add key="serilog:using:File" value="Umbraco.Core" xdt:Transform="SetAttributes(value)" xdt:Locator="Match(key)"/>
<add key="serilog:write-to:File.shared" xdt:Transform="Remove" xdt:Locator="Match(key)"/>
</appSettings>
</configuration>
@@ -0,0 +1,143 @@
<?xml version="1.0"?>
<trees xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<!--Content-->
<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"
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"
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"
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"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<!--Settings-->
<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"
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"
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"
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"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="settings" alias="partialViews" type="Umbraco.Web.Trees.PartialViewsTree, Umbraco.Web"
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"
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"
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"
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"
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"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<!--Developer-->
<add alias="packager" application="developer"
xdt:Locator="Match(application,alias)"
xdt:Transform="Remove" />
<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"
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"
xdt:Locator="Match(application,type)"
xdt:Transform="SetAttributes(alias,sortOrder)" />
<!-- Yes, set the sortOrder again, like above because.. sometimes apparently we already have a dataTypes node and we can't remove more than one.. if they're equal though (same alias,application and sortOrder) it doesn't throw an error -->
<add sortOrder="1" alias="dataTypes" application="developer"
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"
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"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="developer" alias="partialViewMacros" type="Umbraco.Web.Trees.PartialViewMacrosTree, Umbraco.Web"
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"
xdt:Locator="Match(application,alias)"
xdt:Transform="InsertIfMissing" />
<add application="developer" alias="python"
xdt:Locator="Match(application,alias)"
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"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="users" alias="userTypes"
xdt:Locator="Match(application,alias)"
xdt:Transform="Remove" />
<add application="users" alias="userPermissions"
xdt:Locator="Match(application,alias)"
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"
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"
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"
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"
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"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add alias="yourTasks" application="translation"
xdt:Locator="Match(application,alias)"
xdt:Transform="RemoveAttributes(action)" />
</trees>
-44
View File
@@ -1,44 +0,0 @@
$uenv=build/build.ps1 -get
$src = "$($uenv.SolutionRoot)\src"
$tmp = $uenv.BuildTemp
$out = $uenv.BuildOutput
$DocFxJson = "$src\ApiDocs\docfx.json"
$DocFxSiteOutput = "$tmp\_site\*.*"
################ Do the UI docs
$uenv.CompileBelle()
"Moving to Umbraco.Web.UI.Client folder"
cd .\src\Umbraco.Web.UI.Client
"Generating the docs and waiting before executing the next commands"
& gulp docs | Out-Null
# change baseUrl
$BaseUrl = "https://our.umbraco.com/apidocs/v8/ui/"
$IndexPath = "./docs/api/index.html"
(Get-Content $IndexPath).replace('location.href.replace(rUrl, indexFile)', "`'" + $BaseUrl + "`'") | Set-Content $IndexPath
# zip it
& $uenv.BuildEnv.Zip a -tzip -r "$out\ui-docs.zip" "$src\Umbraco.Web.UI.Client\docs\api\*.*"
################ Do the c# docs
# Build the solution in debug mode
$SolutionPath = Join-Path -Path $src -ChildPath "umbraco.sln"
#$uenv.CompileUmbraco()
#restore nuget packages
$uenv.RestoreNuGet()
# run DocFx
$DocFx = $uenv.BuildEnv.DocFx
Write-Host "$DocFxJson"
& $DocFx metadata $DocFxJson
& $DocFx build $DocFxJson
# zip it
& $uenv.BuildEnv.Zip a -tzip -r "$out\csharp-docs.zip" $DocFxSiteOutput
+10 -69
View File
@@ -11,21 +11,11 @@
[Alias("loc")]
[switch] $local = $false,
# enable docfx
[Parameter(Mandatory=$false)]
[Alias("doc")]
[switch] $docfx = $false,
# keep the build directories, don't clear them
[Parameter(Mandatory=$false)]
[Alias("c")]
[Alias("cont")]
[switch] $continue = $false,
# execute a command
[Parameter(Mandatory=$false, ValueFromRemainingArguments=$true)]
[String[]]
$command
[switch] $continue = $false
)
# ################################################################
@@ -36,7 +26,7 @@
$ubuild = &"$PSScriptRoot\build-bootstrap.ps1"
if (-not $?) { return }
$ubuild.Boot($PSScriptRoot,
@{ Local = $local; WithDocFx = $docfx },
@{ Local = $local; },
@{ Continue = $continue })
if ($ubuild.OnError()) { return }
@@ -130,7 +120,7 @@
} finally {
Pop-Location
# FIXME: should we filter the log to find errors?
# fixme - should we filter the log to find errors?
#get-content .\build.tmp\belle.log | %{ if ($_ -match "build") { write $_}}
# restore
@@ -187,7 +177,7 @@
{
Write-Host "Prepare Tests"
# FIXME: - idea is to avoid rebuilding everything for 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"
@@ -340,6 +330,9 @@
$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")
@@ -392,13 +385,13 @@
&$this.BuildEnv.NuGet Pack "$nuspecs\UmbracoCms.Core.nuspec" `
-Properties BuildTmp="$($this.BuildTemp)" `
-Version "$($this.Version.Semver.ToString())" `
-Verbosity detailed -outputDirectory "$($this.BuildOutput)" > "$($this.BuildTemp)\nupack.cmscore.log"
-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())" `
-Verbosity detailed -outputDirectory "$($this.BuildOutput)" > "$($this.BuildTemp)\nupack.cmsweb.log"
-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" `
@@ -430,53 +423,6 @@
$this.CopyFile("$($this.SolutionRoot)\build\Azure\azuregalleryrelease.ps1", $this.BuildOutput)
})
$ubuild.DefineMethod("PrepareCSharpDocs",
{
Write-Host "Prepare C# Documentation"
$src = "$($this.SolutionRoot)\src"
$tmp = $this.BuildTemp
$out = $this.BuildOutput
$DocFxJson = Join-Path -Path $src "\ApiDocs\docfx.json"
$DocFxSiteOutput = Join-Path -Path $tmp "\_site\*.*"
#restore nuget packages
$this.RestoreNuGet()
# run DocFx
$DocFx = $this.BuildEnv.DocFx
& $DocFx metadata $DocFxJson
& $DocFx build $DocFxJson
# zip it
& $this.BuildEnv.Zip a -tzip -r "$out\csharp-docs.zip" $DocFxSiteOutput
})
$ubuild.DefineMethod("PrepareAngularDocs",
{
Write-Host "Prepare Angular Documentation"
$src = "$($this.SolutionRoot)\src"
$out = $this.BuildOutput
$this.CompileBelle()
"Moving to Umbraco.Web.UI.Client folder"
cd .\src\Umbraco.Web.UI.Client
"Generating the docs and waiting before executing the next commands"
& gulp docs | Out-Null
# change baseUrl
$BaseUrl = "https://our.umbraco.com/apidocs/v8/ui/"
$IndexPath = "./docs/api/index.html"
(Get-Content $IndexPath).replace('location.href.replace(rUrl, indexFile)', "`'" + $BaseUrl + "`'") | Set-Content $IndexPath
# zip it
& $this.BuildEnv.Zip a -tzip -r "$out\ui-docs.zip" "$src\Umbraco.Web.UI.Client\docs\api\*.*"
})
$ubuild.DefineMethod("Build",
{
$error.Clear()
@@ -508,7 +454,6 @@
if ($this.OnError()) { return }
$this.PostPackageHook()
if ($this.OnError()) { return }
Write-Host "Done"
})
@@ -533,11 +478,7 @@
# run
if (-not $get)
{
if ($command.Length -eq 0)
{
$command = @( "Build" )
}
$ubuild.RunMethod($command);
$ubuild.Build()
if ($ubuild.OnError()) { return }
}
if ($get) { return $ubuild }
+24 -10
View File
@@ -2,20 +2,21 @@
"metadata": [
{
"src": [
{
"src": "../",
{
"files": [
"Umbraco.Core/Umbraco.Core.csproj",
"Umbraco.Web/Umbraco.Web.csproj"
],
"exclude": [
"**/obj/**",
"**/bin/**"
]
"**/bin/**",
"_site/**"
],
"cwd": "../src"
}
],
"dest": "api",
"filter": "docfx.filter.yml"
"dest": "../apidocs/api",
"filter": "../apidocs/docfx.filter.yml"
}
],
"build": {
@@ -34,7 +35,19 @@
"*.md"
],
"exclude": [
"obj/**"
"obj/**",
"_site/**"
]
}
],
"resource": [
{
"files": [
"images/**"
],
"exclude": [
"obj/**",
"_site/**"
]
}
],
@@ -44,7 +57,8 @@
"**.md"
],
"exclude": [
"obj/**"
"obj/**",
"_site/**"
]
}
],
@@ -53,9 +67,9 @@
"_enableSearch": true,
"_disableContribution": false
},
"dest": "../../build.tmp/_site",
"dest": "_site",
"template": [
"default", "umbracotemplate"
]
}
}
}
+3 -3
View File
@@ -2,7 +2,7 @@
using System.Resources;
[assembly: AssemblyCompany("Umbraco")]
[assembly: AssemblyCopyright("Copyright © Umbraco 2019")]
[assembly: AssemblyCopyright("Copyright © Umbraco 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -18,5 +18,5 @@ using System.Resources;
[assembly: AssemblyVersion("8.0.0")]
// these are FYI and changed automatically
[assembly: AssemblyFileVersion("8.2.0")]
[assembly: AssemblyInformationalVersion("8.2.0")]
[assembly: AssemblyFileVersion("8.0.0")]
[assembly: AssemblyInformationalVersion("8.0.0-alpha.58")]
+22 -25
View File
@@ -67,34 +67,31 @@ namespace Umbraco.Core
: new NamedSemaphoreReleaser(_semaphore2);
}
//NOTE: We don't use the "Async" part of this lock at all
//TODO: Remove this and rename this class something like SystemWideLock, then we can re-instate this logic if we ever need an Async lock again
public Task<IDisposable> LockAsync()
{
var wait = _semaphore != null
? _semaphore.WaitAsync()
: _semaphore2.WaitOneAsync();
//public Task<IDisposable> LockAsync()
//{
// var wait = _semaphore != null
// ? _semaphore.WaitAsync()
// : _semaphore2.WaitOneAsync();
return wait.IsCompleted
? _releaserTask ?? Task.FromResult(CreateReleaser()) // anonymous vs named
: wait.ContinueWith((_, state) => (((AsyncLock) state).CreateReleaser()),
this, CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}
// return wait.IsCompleted
// ? _releaserTask ?? Task.FromResult(CreateReleaser()) // anonymous vs named
// : wait.ContinueWith((_, state) => (((AsyncLock) state).CreateReleaser()),
// this, CancellationToken.None,
// TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
//}
public Task<IDisposable> LockAsync(int millisecondsTimeout)
{
var wait = _semaphore != null
? _semaphore.WaitAsync(millisecondsTimeout)
: _semaphore2.WaitOneAsync(millisecondsTimeout);
//public Task<IDisposable> LockAsync(int millisecondsTimeout)
//{
// var wait = _semaphore != null
// ? _semaphore.WaitAsync(millisecondsTimeout)
// : _semaphore2.WaitOneAsync(millisecondsTimeout);
// return wait.IsCompleted
// ? _releaserTask ?? Task.FromResult(CreateReleaser()) // anonymous vs named
// : wait.ContinueWith((_, state) => (((AsyncLock)state).CreateReleaser()),
// this, CancellationToken.None,
// TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
//}
return wait.IsCompleted
? _releaserTask ?? Task.FromResult(CreateReleaser()) // anonymous vs named
: wait.ContinueWith((_, state) => (((AsyncLock)state).CreateReleaser()),
this, CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}
public IDisposable Lock()
{
+1 -1
View File
@@ -8,7 +8,7 @@ namespace Umbraco.Core
public static class Attempt
{
// note:
// cannot rely on overloads only to differentiate between with/without status
// 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>
+1 -1
View File
@@ -121,7 +121,7 @@ namespace Umbraco.Core
}
/// <summary>
/// Implicitly operator to check if the attempt was successful without having to access the 'success' property
/// Implicity operator to check if the attempt was successful without having to access the 'success' property
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
+1 -1
View File
@@ -130,7 +130,7 @@ namespace Umbraco.Core
}
/// <summary>
/// Implicitly operator to check if the attempt was successful without having to access the 'success' property
/// Implicity operator to check if the attempt was successful without having to access the 'success' property
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
+4 -9
View File
@@ -1,10 +1,10 @@
using System;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Web;
using Umbraco.Core;
// no binding redirect for now = de-activate
//[assembly: PreApplicationStartMethod(typeof(BindingRedirects), "Initialize")]
[assembly: PreApplicationStartMethod(typeof(BindingRedirects), "Initialize")]
namespace Umbraco.Core
{
@@ -18,7 +18,7 @@ namespace Umbraco.Core
// this only gets called when an assembly can't be resolved
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
}
/// <summary>
/// This is used to do an assembly binding redirect via code - normally required due to signature changes in assemblies
/// </summary>
@@ -27,19 +27,14 @@ namespace Umbraco.Core
/// <returns></returns>
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
// When an assembly can't be resolved. In here we can do magic with the assembly name and try loading another.
// keep here for reference - we don't use AutoMapper
/*
//AutoMapper:
// this is used for loading a signed assembly of AutoMapper (v. 3.1+) without having to recompile old code.
// 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
if (args.Name.StartsWith("AutoMapper") && args.Name.EndsWith("PublicKeyToken=null"))
return Assembly.Load(args.Name.Replace(", PublicKeyToken=null", ", PublicKeyToken=be96cd2c38ef1005"));
*/
return null;
}
}
}
-86
View File
@@ -1,86 +0,0 @@
using System;
using System.Web;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Represents the application caches.
/// </summary>
public class AppCaches
{
/// <summary>
/// Initializes a new instance of the <see cref="AppCaches"/> for use in a web application.
/// </summary>
public AppCaches()
: this(HttpRuntime.Cache)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="AppCaches"/> for use in a web application.
/// </summary>
public AppCaches(System.Web.Caching.Cache cache)
: this(
new WebCachingAppCache(cache),
new HttpRequestAppCache(),
new IsolatedCaches(t => new ObjectCacheAppCache()))
{ }
/// <summary>
/// Initializes a new instance of the <see cref="AppCaches"/> with cache providers.
/// </summary>
public AppCaches(
IAppPolicyCache runtimeCache,
IAppCache requestCache,
IsolatedCaches isolatedCaches)
{
RuntimeCache = runtimeCache ?? throw new ArgumentNullException(nameof(runtimeCache));
RequestCache = requestCache ?? throw new ArgumentNullException(nameof(requestCache));
IsolatedCaches = isolatedCaches ?? throw new ArgumentNullException(nameof(isolatedCaches));
}
/// <summary>
/// Gets the special disabled instance.
/// </summary>
/// <remarks>
/// <para>When used by repositories, all cache policies apply, but the underlying caches do not cache anything.</para>
/// <para>Used by tests.</para>
/// </remarks>
public static AppCaches Disabled { get; } = new AppCaches(NoAppCache.Instance, NoAppCache.Instance, new IsolatedCaches(_ => NoAppCache.Instance));
/// <summary>
/// Gets the special no-cache instance.
/// </summary>
/// <remarks>
/// <para>When used by repositories, all cache policies are bypassed.</para>
/// <para>Used by repositories that do no cache.</para>
/// </remarks>
public static AppCaches NoCache { get; } = new AppCaches(NoAppCache.Instance, NoAppCache.Instance, new IsolatedCaches(_ => NoAppCache.Instance));
/// <summary>
/// Gets the per-request cache.
/// </summary>
/// <remarks>
/// <para>The per-request caches works on top of the current HttpContext items.</para>
/// <para>Outside a web environment, the behavior of that cache is unspecified.</para>
/// </remarks>
public IAppCache RequestCache { get; }
/// <summary>
/// Gets the runtime cache.
/// </summary>
/// <remarks>
/// <para>The runtime cache is the main application cache.</para>
/// </remarks>
public IAppPolicyCache RuntimeCache { get; }
/// <summary>
/// Gets the isolated caches.
/// </summary>
/// <remarks>
/// <para>Isolated caches are used by e.g. repositories, to ensure that each cached entity
/// type has its own cache, so that lookups are fast and the repository does not need to
/// search through all keys on a global scale.</para>
/// </remarks>
public IsolatedCaches IsolatedCaches { get; }
}
}
@@ -1,74 +0,0 @@
using System;
using System.Collections.Concurrent;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Provides a base class for implementing a dictionary of <see cref="IAppPolicyCache"/>.
/// </summary>
/// <typeparam name="TKey">The type of the dictionary key.</typeparam>
public abstract class AppPolicedCacheDictionary<TKey>
{
private readonly ConcurrentDictionary<TKey, IAppPolicyCache> _caches = new ConcurrentDictionary<TKey, IAppPolicyCache>();
/// <summary>
/// Initializes a new instance of the <see cref="AppPolicedCacheDictionary{TKey}"/> class.
/// </summary>
/// <param name="cacheFactory"></param>
protected AppPolicedCacheDictionary(Func<TKey, IAppPolicyCache> cacheFactory)
{
CacheFactory = cacheFactory;
}
/// <summary>
/// Gets the internal cache factory, for tests only!
/// </summary>
internal readonly Func<TKey, IAppPolicyCache> CacheFactory;
/// <summary>
/// Gets or creates a cache.
/// </summary>
public IAppPolicyCache GetOrCreate(TKey key)
=> _caches.GetOrAdd(key, k => CacheFactory(k));
/// <summary>
/// Tries to get a cache.
/// </summary>
public Attempt<IAppPolicyCache> Get(TKey key)
=> _caches.TryGetValue(key, out var cache) ? Attempt.Succeed(cache) : Attempt.Fail<IAppPolicyCache>();
/// <summary>
/// Removes a cache.
/// </summary>
public void Remove(TKey key)
{
_caches.TryRemove(key, out _);
}
/// <summary>
/// Removes all caches.
/// </summary>
public void RemoveAll()
{
_caches.Clear();
}
/// <summary>
/// Clears a cache.
/// </summary>
public void ClearCache(TKey key)
{
if (_caches.TryGetValue(key, out var cache))
cache.Clear();
}
/// <summary>
/// Clears all caches.
/// </summary>
public void ClearAllCaches()
{
foreach (var cache in _caches.Values)
cache.Clear();
}
}
}
+90
View File
@@ -0,0 +1,90 @@
using System;
using System.Web;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Represents the application-wide caches.
/// </summary>
public class CacheHelper
{
/// <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>
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>
public CacheHelper(
IRuntimeCacheProvider httpCacheProvider,
ICacheProvider staticCacheProvider,
ICacheProvider requestCacheProvider,
IsolatedRuntimeCache isolatedCacheManager)
{
RuntimeCache = httpCacheProvider ?? throw new ArgumentNullException(nameof(httpCacheProvider));
StaticCache = staticCacheProvider ?? throw new ArgumentNullException(nameof(staticCacheProvider));
RequestCache = requestCacheProvider ?? throw new ArgumentNullException(nameof(requestCacheProvider));
IsolatedRuntimeCache = isolatedCacheManager ?? throw new ArgumentNullException(nameof(isolatedCacheManager));
}
/// <summary>
/// Gets the special disabled instance.
/// </summary>
/// <remarks>
/// <para>When used by repositories, all cache policies apply, but the underlying caches do not cache anything.</para>
/// <para>Used by tests.</para>
/// </remarks>
public static CacheHelper Disabled { get; } = new CacheHelper(NullCacheProvider.Instance, NullCacheProvider.Instance, NullCacheProvider.Instance, new IsolatedRuntimeCache(_ => NullCacheProvider.Instance));
/// <summary>
/// Gets the special no-cache instance.
/// </summary>
/// <remarks>
/// <para>When used by repositories, all cache policies are bypassed.</para>
/// <para>Used by repositories that do no cache.</para>
/// </remarks>
public static CacheHelper NoCache { get; } = new CacheHelper(NullCacheProvider.Instance, NullCacheProvider.Instance, NullCacheProvider.Instance, new IsolatedRuntimeCache(_ => NullCacheProvider.Instance));
/// <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; }
}
}
+2 -2
View File
@@ -5,10 +5,10 @@
/// </summary>
public static class CacheKeys
{
public const string ApplicationTreeCacheKey = "ApplicationTreeCache"; // used by ApplicationTreeService
public const string ApplicationsCacheKey = "ApplicationCache"; // used by SectionService
// TODO: this one can probably be removed
public const string TemplateFrontEndCacheKey = "template";
public const string TemplateFrontEndCacheKey = "template"; // fixme usage?
public const string MacroContentCacheKey = "macroContent_"; // used in MacroRenderers
}
@@ -8,9 +8,9 @@ namespace Umbraco.Core.Cache
/// <summary>
/// Extensions for strongly typed access
/// </summary>
public static class AppCacheExtensions
public static class CacheProviderExtensions
{
public static T GetCacheItem<T>(this IAppPolicyCache provider,
public static T GetCacheItem<T>(this IRuntimeCacheProvider provider,
string cacheKey,
Func<T> getCacheItem,
TimeSpan? timeout,
@@ -19,11 +19,11 @@ namespace Umbraco.Core.Cache
CacheItemRemovedCallback removedCallback = null,
string[] dependentFiles = null)
{
var result = provider.Get(cacheKey, () => getCacheItem(), timeout, isSliding, priority, removedCallback, dependentFiles);
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 IAppPolicyCache provider,
public static void InsertCacheItem<T>(this IRuntimeCacheProvider provider,
string cacheKey,
Func<T> getCacheItem,
TimeSpan? timeout = null,
@@ -32,24 +32,24 @@ namespace Umbraco.Core.Cache
CacheItemRemovedCallback removedCallback = null,
string[] dependentFiles = null)
{
provider.Insert(cacheKey, () => getCacheItem(), timeout, isSliding, priority, removedCallback, dependentFiles);
provider.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, isSliding, priority, removedCallback, dependentFiles);
}
public static IEnumerable<T> GetCacheItemsByKeySearch<T>(this IAppCache provider, string keyStartsWith)
public static IEnumerable<T> GetCacheItemsByKeySearch<T>(this ICacheProvider provider, string keyStartsWith)
{
var result = provider.SearchByKey(keyStartsWith);
var result = provider.GetCacheItemsByKeySearch(keyStartsWith);
return result.Select(x => x.TryConvertTo<T>().Result);
}
public static IEnumerable<T> GetCacheItemsByKeyExpression<T>(this IAppCache provider, string regexString)
public static IEnumerable<T> GetCacheItemsByKeyExpression<T>(this ICacheProvider provider, string regexString)
{
var result = provider.SearchByRegex(regexString);
var result = provider.GetCacheItemsByKeyExpression(regexString);
return result.Select(x => x.TryConvertTo<T>().Result);
}
public static T GetCacheItem<T>(this IAppCache provider, string cacheKey)
public static T GetCacheItem<T>(this ICacheProvider provider, string cacheKey)
{
var result = provider.Get(cacheKey);
var result = provider.GetCacheItem(cacheKey);
if (result == null)
{
return default(T);
@@ -57,9 +57,9 @@ namespace Umbraco.Core.Cache
return result.TryConvertTo<T>().Result;
}
public static T GetCacheItem<T>(this IAppCache provider, string cacheKey, Func<T> getCacheItem)
public static T GetCacheItem<T>(this ICacheProvider provider, string cacheKey, Func<T> getCacheItem)
{
var result = provider.Get(cacheKey, () => getCacheItem());
var result = provider.GetCacheItem(cacheKey, () => getCacheItem());
if (result == null)
{
return default(T);
+5 -5
View File
@@ -16,10 +16,10 @@ namespace Umbraco.Core.Cache
/// <summary>
/// Initializes a new instance of the <see cref="CacheRefresherBase{TInstanceType}"/>.
/// </summary>
/// <param name="appCaches">A cache helper.</param>
protected CacheRefresherBase(AppCaches appCaches)
/// <param name="cacheHelper">A cache helper.</param>
protected CacheRefresherBase(CacheHelper cacheHelper)
{
AppCaches = appCaches;
CacheHelper = cacheHelper;
}
/// <summary>
@@ -93,7 +93,7 @@ namespace Umbraco.Core.Cache
/// <summary>
/// Gets the cache helper.
/// </summary>
protected AppCaches AppCaches { get; }
protected CacheHelper CacheHelper { get; }
/// <summary>
/// Clears the cache for all repository entities of a specified type.
@@ -102,7 +102,7 @@ namespace Umbraco.Core.Cache
protected void ClearAllIsolatedCacheByEntityType<TEntity>()
where TEntity : class, IEntity
{
AppCaches.IsolatedCaches.ClearCache<TEntity>();
CacheHelper.IsolatedRuntimeCache.ClearCache<TEntity>();
}
/// <summary>
-157
View File
@@ -1,157 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Caching;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Implements <see cref="IAppPolicyCache"/> by wrapping an inner other <see cref="IAppPolicyCache"/>
/// instance, and ensuring that all inserts and returns are deep cloned copies of the cache item,
/// when the item is deep-cloneable.
/// </summary>
internal class DeepCloneAppCache : IAppPolicyCache
{
/// <summary>
/// Initializes a new instance of the <see cref="DeepCloneAppCache"/> class.
/// </summary>
public DeepCloneAppCache(IAppPolicyCache innerCache)
{
var type = typeof (DeepCloneAppCache);
if (innerCache.GetType() == type)
throw new InvalidOperationException($"A {type} cannot wrap another instance of itself.");
InnerCache = innerCache;
}
/// <summary>
/// Gets the inner cache.
/// </summary>
public IAppPolicyCache InnerCache { get; }
/// <inheritdoc />
public object Get(string key)
{
var item = InnerCache.Get(key);
return CheckCloneableAndTracksChanges(item);
}
/// <inheritdoc />
public object Get(string key, Func<object> factory)
{
var cached = InnerCache.Get(key, () =>
{
var result = FastDictionaryAppCacheBase.GetSafeLazy(factory);
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
// do not store null values (backward compat), clone / reset to go into the cache
return value == null ? null : CheckCloneableAndTracksChanges(value);
});
return CheckCloneableAndTracksChanges(cached);
}
/// <inheritdoc />
public IEnumerable<object> SearchByKey(string keyStartsWith)
{
return InnerCache.SearchByKey(keyStartsWith)
.Select(CheckCloneableAndTracksChanges);
}
/// <inheritdoc />
public IEnumerable<object> SearchByRegex(string regex)
{
return InnerCache.SearchByRegex(regex)
.Select(CheckCloneableAndTracksChanges);
}
/// <inheritdoc />
public object Get(string key, Func<object> factory, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
var cached = InnerCache.Get(key, () =>
{
var result = FastDictionaryAppCacheBase.GetSafeLazy(factory);
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
// do not store null values (backward compat), clone / reset to go into the cache
return value == null ? null : CheckCloneableAndTracksChanges(value);
// clone / reset to go into the cache
}, timeout, isSliding, priority, removedCallback, dependentFiles);
// clone / reset to go into the cache
return CheckCloneableAndTracksChanges(cached);
}
/// <inheritdoc />
public void Insert(string key, Func<object> factory, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
InnerCache.Insert(key, () =>
{
var result = FastDictionaryAppCacheBase.GetSafeLazy(factory);
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
// do not store null values (backward compat), clone / reset to go into the cache
return value == null ? null : CheckCloneableAndTracksChanges(value);
}, timeout, isSliding, priority, removedCallback, dependentFiles);
}
/// <inheritdoc />
public void Clear()
{
InnerCache.Clear();
}
/// <inheritdoc />
public void Clear(string key)
{
InnerCache.Clear(key);
}
/// <inheritdoc />
public void ClearOfType(string typeName)
{
InnerCache.ClearOfType(typeName);
}
/// <inheritdoc />
public void ClearOfType<T>()
{
InnerCache.ClearOfType<T>();
}
/// <inheritdoc />
public void ClearOfType<T>(Func<string, T, bool> predicate)
{
InnerCache.ClearOfType<T>(predicate);
}
/// <inheritdoc />
public void ClearByKey(string keyStartsWith)
{
InnerCache.ClearByKey(keyStartsWith);
}
/// <inheritdoc />
public void ClearByRegex(string regex)
{
InnerCache.ClearByRegex(regex);
}
private static object CheckCloneableAndTracksChanges(object input)
{
if (input is IDeepCloneable cloneable)
{
input = cloneable.DeepClone();
}
// reset dirty initial properties
if (input is IRememberBeingDirty tracksChanges)
{
tracksChanges.ResetDirtyProperties(false);
input = tracksChanges;
}
return input;
}
}
}
@@ -0,0 +1,155 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Caching;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Interface describing this cache provider as a wrapper for another
/// </summary>
internal interface IRuntimeCacheProviderWrapper
{
IRuntimeCacheProvider InnerProvider { get; }
}
/// <summary>
/// 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 DeepCloneRuntimeCacheProvider(IRuntimeCacheProvider innerProvider)
{
var type = typeof (DeepCloneRuntimeCacheProvider);
if (innerProvider.GetType() == type)
throw new InvalidOperationException($"A {type} cannot wrap another instance of {type}.");
InnerProvider = innerProvider;
}
#region Clear - doesn't require any changes
public void ClearAllCache()
{
InnerProvider.ClearAllCache();
}
public void ClearCacheItem(string key)
{
InnerProvider.ClearCacheItem(key);
}
public void ClearCacheObjectTypes(string typeName)
{
InnerProvider.ClearCacheObjectTypes(typeName);
}
public void ClearCacheObjectTypes<T>()
{
InnerProvider.ClearCacheObjectTypes<T>();
}
public void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
{
InnerProvider.ClearCacheObjectTypes<T>(predicate);
}
public void ClearCacheByKeySearch(string keyStartsWith)
{
InnerProvider.ClearCacheByKeySearch(keyStartsWith);
}
public void ClearCacheByKeyExpression(string regexString)
{
InnerProvider.ClearCacheByKeyExpression(regexString);
}
#endregion
public IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
{
return InnerProvider.GetCacheItemsByKeySearch(keyStartsWith)
.Select(CheckCloneableAndTracksChanges);
}
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
{
return InnerProvider.GetCacheItemsByKeyExpression(regexString)
.Select(CheckCloneableAndTracksChanges);
}
public object GetCacheItem(string cacheKey)
{
var item = InnerProvider.GetCacheItem(cacheKey);
return CheckCloneableAndTracksChanges(item);
}
public object GetCacheItem(string cacheKey, Func<object> getCacheItem)
{
var cached = InnerProvider.GetCacheItem(cacheKey, () =>
{
var result = DictionaryCacheProviderBase.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 null; // do not store null values (backward compat)
return CheckCloneableAndTracksChanges(value);
});
return CheckCloneableAndTracksChanges(cached);
}
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
var cached = InnerProvider.GetCacheItem(cacheKey, () =>
{
var result = DictionaryCacheProviderBase.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 null; // do not store null values (backward compat)
// clone / reset to go into the cache
return CheckCloneableAndTracksChanges(value);
}, timeout, isSliding, priority, removedCallback, dependentFiles);
// clone / reset to go into the cache
return CheckCloneableAndTracksChanges(cached);
}
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
InnerProvider.InsertCacheItem(cacheKey, () =>
{
var result = DictionaryCacheProviderBase.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 null; // do not store null values (backward compat)
// clone / reset to go into the cache
return CheckCloneableAndTracksChanges(value);
}, timeout, isSliding, priority, removedCallback, dependentFiles);
}
private static object CheckCloneableAndTracksChanges(object input)
{
var cloneable = input as IDeepCloneable;
if (cloneable != null)
{
input = cloneable.DeepClone();
}
// reset dirty initial properties (U4-1946)
var tracksChanges = input as IRememberBeingDirty;
if (tracksChanges != null)
{
tracksChanges.ResetDirtyProperties(false);
input = tracksChanges;
}
return input;
}
}
}
@@ -23,7 +23,7 @@ namespace Umbraco.Core.Cache
private static readonly TEntity[] EmptyEntities = new TEntity[0]; // const
private readonly RepositoryCachePolicyOptions _options;
public DefaultRepositoryCachePolicy(IAppPolicyCache cache, IScopeAccessor scopeAccessor, RepositoryCachePolicyOptions options)
public DefaultRepositoryCachePolicy(IRuntimeCacheProvider cache, IScopeAccessor scopeAccessor, RepositoryCachePolicyOptions options)
: base(cache, scopeAccessor)
{
_options = options ?? throw new ArgumentNullException(nameof(options));
@@ -42,7 +42,7 @@ namespace Umbraco.Core.Cache
protected virtual void InsertEntity(string cacheKey, TEntity entity)
{
Cache.Insert(cacheKey, () => entity, TimeSpan.FromMinutes(5), true);
Cache.InsertCacheItem(cacheKey, () => entity, TimeSpan.FromMinutes(5), true);
}
protected virtual void InsertEntities(TId[] ids, TEntity[] entities)
@@ -52,7 +52,7 @@ namespace Umbraco.Core.Cache
// getting all of them, and finding nothing.
// if we can cache a zero count, cache an empty array,
// for as long as the cache is not cleared (no expiration)
Cache.Insert(GetEntityTypeCacheKey(), () => EmptyEntities);
Cache.InsertCacheItem(GetEntityTypeCacheKey(), () => EmptyEntities);
}
else
{
@@ -60,7 +60,7 @@ namespace Umbraco.Core.Cache
foreach (var entity in entities)
{
var capture = entity;
Cache.Insert(GetEntityCacheKey(entity.Id), () => capture, TimeSpan.FromMinutes(5), true);
Cache.InsertCacheItem(GetEntityCacheKey(entity.Id), () => capture, TimeSpan.FromMinutes(5), true);
}
}
}
@@ -77,21 +77,21 @@ namespace Umbraco.Core.Cache
// just to be safe, we cannot cache an item without an identity
if (entity.HasIdentity)
{
Cache.Insert(GetEntityCacheKey(entity.Id), () => entity, TimeSpan.FromMinutes(5), true);
Cache.InsertCacheItem(GetEntityCacheKey(entity.Id), () => entity, TimeSpan.FromMinutes(5), true);
}
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
Cache.Clear(GetEntityTypeCacheKey());
Cache.ClearCacheItem(GetEntityTypeCacheKey());
}
catch
{
// if an exception is thrown we need to remove the entry from cache,
// this is ONLY a work around because of the way
// that we cache entities: http://issues.umbraco.org/issue/U4-4259
Cache.Clear(GetEntityCacheKey(entity.Id));
Cache.ClearCacheItem(GetEntityCacheKey(entity.Id));
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
Cache.Clear(GetEntityTypeCacheKey());
Cache.ClearCacheItem(GetEntityTypeCacheKey());
throw;
}
@@ -109,21 +109,21 @@ namespace Umbraco.Core.Cache
// just to be safe, we cannot cache an item without an identity
if (entity.HasIdentity)
{
Cache.Insert(GetEntityCacheKey(entity.Id), () => entity, TimeSpan.FromMinutes(5), true);
Cache.InsertCacheItem(GetEntityCacheKey(entity.Id), () => entity, TimeSpan.FromMinutes(5), true);
}
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
Cache.Clear(GetEntityTypeCacheKey());
Cache.ClearCacheItem(GetEntityTypeCacheKey());
}
catch
{
// if an exception is thrown we need to remove the entry from cache,
// this is ONLY a work around because of the way
// that we cache entities: http://issues.umbraco.org/issue/U4-4259
Cache.Clear(GetEntityCacheKey(entity.Id));
Cache.ClearCacheItem(GetEntityCacheKey(entity.Id));
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
Cache.Clear(GetEntityTypeCacheKey());
Cache.ClearCacheItem(GetEntityTypeCacheKey());
throw;
}
@@ -142,9 +142,9 @@ namespace Umbraco.Core.Cache
{
// whatever happens, clear the cache
var cacheKey = GetEntityCacheKey(entity.Id);
Cache.Clear(cacheKey);
Cache.ClearCacheItem(cacheKey);
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
Cache.Clear(GetEntityTypeCacheKey());
Cache.ClearCacheItem(GetEntityTypeCacheKey());
}
}
@@ -238,7 +238,7 @@ namespace Umbraco.Core.Cache
/// <inheritdoc />
public override void ClearAll()
{
Cache.ClearByKey(GetEntityTypeCacheKey());
Cache.ClearCacheByKeySearch(GetEntityTypeCacheKey());
}
}
}
@@ -1,96 +0,0 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Implements <see cref="IAppCache"/> on top of a concurrent dictionary.
/// </summary>
public class DictionaryAppCache : IAppCache
{
/// <summary>
/// Gets the internal items dictionary, for tests only!
/// </summary>
internal readonly ConcurrentDictionary<string, object> Items = new ConcurrentDictionary<string, object>();
/// <inheritdoc />
public virtual object Get(string key)
{
return Items.TryGetValue(key, out var value) ? value : null;
}
/// <inheritdoc />
public virtual object Get(string key, Func<object> factory)
{
return Items.GetOrAdd(key, _ => factory());
}
/// <inheritdoc />
public virtual IEnumerable<object> SearchByKey(string keyStartsWith)
{
var items = new List<object>();
foreach (var (key, value) in Items)
if (key.InvariantStartsWith(keyStartsWith))
items.Add(value);
return items;
}
/// <inheritdoc />
public IEnumerable<object> SearchByRegex(string regex)
{
var compiled = new Regex(regex, RegexOptions.Compiled);
var items = new List<object>();
foreach (var (key, value) in Items)
if (compiled.IsMatch(key))
items.Add(value);
return items;
}
/// <inheritdoc />
public virtual void Clear()
{
Items.Clear();
}
/// <inheritdoc />
public virtual void Clear(string key)
{
Items.TryRemove(key, out _);
}
/// <inheritdoc />
public virtual void ClearOfType(string typeName)
{
Items.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType().ToString().InvariantEquals(typeName));
}
/// <inheritdoc />
public virtual void ClearOfType<T>()
{
var typeOfT = typeof(T);
Items.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT);
}
/// <inheritdoc />
public virtual void ClearOfType<T>(Func<string, T, bool> predicate)
{
var typeOfT = typeof(T);
Items.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT && predicate(kvp.Key, (T)kvp.Value));
}
/// <inheritdoc />
public virtual void ClearByKey(string keyStartsWith)
{
Items.RemoveAll(kvp => kvp.Key.InvariantStartsWith(keyStartsWith));
}
/// <inheritdoc />
public virtual void ClearByRegex(string regex)
{
var compiled = new Regex(regex, RegexOptions.Compiled);
Items.RemoveAll(kvp => compiled.IsMatch(kvp.Key));
}
}
}
@@ -7,130 +7,79 @@ using Umbraco.Core.Composing;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Implements a fast <see cref="IAppCache"/> on top of a concurrent dictionary.
/// </summary>
internal class FastDictionaryAppCache : IAppCache
internal class DictionaryCacheProvider : ICacheProvider
{
/// <summary>
/// Gets the internal items dictionary, for tests only!
/// </summary>
internal readonly ConcurrentDictionary<string, Lazy<object>> Items = new ConcurrentDictionary<string, Lazy<object>>();
private readonly ConcurrentDictionary<string, Lazy<object>> _items
= new ConcurrentDictionary<string, Lazy<object>>();
/// <inheritdoc />
public object Get(string cacheKey)
// for tests
internal ConcurrentDictionary<string, Lazy<object>> Items => _items;
public void ClearAllCache()
{
Items.TryGetValue(cacheKey, out var result); // else null
return result == null ? null : FastDictionaryAppCacheBase.GetSafeLazyValue(result); // return exceptions as null
_items.Clear();
}
/// <inheritdoc />
public object Get(string cacheKey, Func<object> getCacheItem)
public void ClearCacheItem(string key)
{
var result = Items.GetOrAdd(cacheKey, k => FastDictionaryAppCacheBase.GetSafeLazy(getCacheItem));
var value = result.Value; // will not throw (safe lazy)
if (!(value is FastDictionaryAppCacheBase.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
_items.TryRemove(key, out _);
}
/// <inheritdoc />
public IEnumerable<object> SearchByKey(string keyStartsWith)
{
return Items
.Where(kvp => kvp.Key.InvariantStartsWith(keyStartsWith))
.Select(kvp => FastDictionaryAppCacheBase.GetSafeLazyValue(kvp.Value))
.Where(x => x != null);
}
/// <inheritdoc />
public IEnumerable<object> SearchByRegex(string regex)
{
var compiled = new Regex(regex, RegexOptions.Compiled);
return Items
.Where(kvp => compiled.IsMatch(kvp.Key))
.Select(kvp => FastDictionaryAppCacheBase.GetSafeLazyValue(kvp.Value))
.Where(x => x != null);
}
/// <inheritdoc />
public void Clear()
{
Items.Clear();
}
/// <inheritdoc />
public void Clear(string key)
{
Items.TryRemove(key, out _);
}
/// <inheritdoc />
public void ClearOfType(string typeName)
public void ClearCacheObjectTypes(string typeName)
{
var type = TypeFinder.GetTypeByName(typeName);
if (type == null) return;
var isInterface = type.IsInterface;
foreach (var kvp in Items
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 = FastDictionaryAppCacheBase.GetSafeLazyValue(x.Value, true);
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 _);
_items.TryRemove(kvp.Key, out _);
}
/// <inheritdoc />
public void ClearOfType<T>()
public void ClearCacheObjectTypes<T>()
{
var typeOfT = typeof(T);
var isInterface = typeOfT.IsInterface;
foreach (var kvp in Items
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 = FastDictionaryAppCacheBase.GetSafeLazyValue(x.Value, true);
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 _);
_items.TryRemove(kvp.Key, out _);
}
/// <inheritdoc />
public void ClearOfType<T>(Func<string, T, bool> predicate)
public void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
{
var typeOfT = typeof(T);
var isInterface = typeOfT.IsInterface;
foreach (var kvp in Items
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 = FastDictionaryAppCacheBase.GetSafeLazyValue(x.Value, true);
var value = DictionaryCacheProviderBase.GetSafeLazyValue(x.Value, true);
if (value == null) return true;
// if T is an interface remove anything that implements that interface
@@ -139,24 +88,60 @@ namespace Umbraco.Core.Cache
// run predicate on the 'public key' part only, ie without prefix
&& predicate(x.Key, (T)value);
}))
Items.TryRemove(kvp.Key, out _);
_items.TryRemove(kvp.Key, out _);
}
/// <inheritdoc />
public void ClearByKey(string keyStartsWith)
public void ClearCacheByKeySearch(string keyStartsWith)
{
foreach (var ikvp in Items
foreach (var ikvp in _items
.Where(kvp => kvp.Key.InvariantStartsWith(keyStartsWith)))
Items.TryRemove(ikvp.Key, out _);
_items.TryRemove(ikvp.Key, out _);
}
/// <inheritdoc />
public void ClearByRegex(string regex)
public void ClearCacheByKeyExpression(string regexString)
{
var compiled = new Regex(regex, RegexOptions.Compiled);
foreach (var ikvp in Items
.Where(kvp => compiled.IsMatch(kvp.Key)))
Items.TryRemove(ikvp.Key, out _);
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
}
}
}
@@ -8,10 +8,7 @@ using Umbraco.Core.Composing;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Provides a base class to fast, dictionary-based <see cref="IAppCache"/> implementations.
/// </summary>
internal abstract class FastDictionaryAppCacheBase : IAppCache
internal abstract class DictionaryCacheProviderBase : ICacheProvider
{
// prefix cache keys so we know which one are ours
protected const string CacheItemPrefix = "umbrtmche";
@@ -19,75 +16,82 @@ namespace Umbraco.Core.Cache
// an object that represent a value that has not been created yet
protected internal static readonly object ValueNotCreated = new object();
#region IAppCache
// 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);
/// <inheritdoc />
public virtual object Get(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)
{
key = GetCacheKey(key);
Lazy<object> result;
try
{
EnterReadLock();
result = GetEntry(key) as Lazy<object>; // null if key not found
}
finally
{
ExitReadLock();
}
return result == null ? null : GetSafeLazyValue(result); // return exceptions as null
return string.Format("{0}-{1}", CacheItemPrefix, key);
}
/// <inheritdoc />
public abstract object Get(string key, Func<object> factory);
/// <inheritdoc />
public virtual IEnumerable<object> SearchByKey(string keyStartsWith)
protected internal static Lazy<object> GetSafeLazy(Func<object> getCacheItem)
{
var plen = CacheItemPrefix.Length + 1;
IEnumerable<DictionaryEntry> entries;
try
// 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>(() =>
{
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
try
{
return getCacheItem();
}
catch (Exception e)
{
return new ExceptionHolder(ExceptionDispatchInfo.Capture(e));
}
});
}
/// <inheritdoc />
public virtual IEnumerable<object> SearchByRegex(string regex)
protected internal static object GetSafeLazyValue(Lazy<object> lazy, bool onlyIfValueIsCreated = false)
{
const string prefix = CacheItemPrefix + "-";
var compiled = new Regex(regex, RegexOptions.Compiled);
var plen = prefix.Length;
IEnumerable<DictionaryEntry> entries;
// 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
{
EnterReadLock();
entries = GetDictionaryEntries()
.Where(x => compiled.IsMatch(((string)x.Key).Substring(plen)))
.ToArray(); // evaluate while locked
return lazy.Value;
}
finally
catch (InvalidOperationException e)
{
ExitReadLock();
throw new InvalidOperationException("The method that computes a value for the cache has tried to read that value from the cache.", e);
}
return entries
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
.Where(x => x != null); // backward compatible, don't store null values in the cache
}
/// <inheritdoc />
public virtual void Clear()
internal class ExceptionHolder
{
public ExceptionHolder(ExceptionDispatchInfo e)
{
Exception = e;
}
public ExceptionDispatchInfo Exception { get; }
}
#region Clear
public virtual void ClearAllCache()
{
try
{
@@ -102,8 +106,7 @@ namespace Umbraco.Core.Cache
}
}
/// <inheritdoc />
public virtual void Clear(string key)
public virtual void ClearCacheItem(string key)
{
var cacheKey = GetCacheKey(key);
try
@@ -117,8 +120,7 @@ namespace Umbraco.Core.Cache
}
}
/// <inheritdoc />
public virtual void ClearOfType(string typeName)
public virtual void ClearCacheObjectTypes(string typeName)
{
var type = TypeFinder.GetTypeByName(typeName);
if (type == null) return;
@@ -147,8 +149,7 @@ namespace Umbraco.Core.Cache
}
}
/// <inheritdoc />
public virtual void ClearOfType<T>()
public virtual void ClearCacheObjectTypes<T>()
{
var typeOfT = typeof(T);
var isInterface = typeOfT.IsInterface;
@@ -177,8 +178,7 @@ namespace Umbraco.Core.Cache
}
}
/// <inheritdoc />
public virtual void ClearOfType<T>(Func<string, T, bool> predicate)
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
{
var typeOfT = typeof(T);
var isInterface = typeOfT.IsInterface;
@@ -210,8 +210,7 @@ namespace Umbraco.Core.Cache
}
}
/// <inheritdoc />
public virtual void ClearByKey(string keyStartsWith)
public virtual void ClearCacheByKeySearch(string keyStartsWith)
{
var plen = CacheItemPrefix.Length + 1;
try
@@ -228,16 +227,14 @@ namespace Umbraco.Core.Cache
}
}
/// <inheritdoc />
public virtual void ClearByRegex(string regex)
public virtual void ClearCacheByKeyExpression(string regexString)
{
var compiled = new Regex(regex, RegexOptions.Compiled);
var plen = CacheItemPrefix.Length + 1;
try
{
EnterWriteLock();
foreach (var entry in GetDictionaryEntries()
.Where(x => compiled.IsMatch(((string)x.Key).Substring(plen)))
.Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString))
.ToArray())
RemoveEntry((string) entry.Key);
}
@@ -249,81 +246,68 @@ namespace Umbraco.Core.Cache
#endregion
#region Dictionary
#region Get
// manipulate 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)
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
{
return $"{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 differentiate 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
var plen = CacheItemPrefix.Length + 1;
IEnumerable<DictionaryEntry> entries;
try
{
return lazy.Value;
EnterReadLock();
entries = GetDictionaryEntries()
.Where(x => ((string)x.Key).Substring(plen).InvariantStartsWith(keyStartsWith))
.ToArray(); // evaluate while locked
}
catch (InvalidOperationException e)
finally
{
throw new InvalidOperationException("The method that computes a value for the cache has tried to read that value from the cache.", e);
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
}
internal class ExceptionHolder
public virtual IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
{
public ExceptionHolder(ExceptionDispatchInfo e)
const string prefix = CacheItemPrefix + "-";
var plen = prefix.Length;
IEnumerable<DictionaryEntry> entries;
try
{
Exception = e;
EnterReadLock();
entries = GetDictionaryEntries()
.Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString))
.ToArray(); // evaluate while locked
}
public ExceptionDispatchInfo Exception { get; }
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
}
}
@@ -24,7 +24,7 @@ namespace Umbraco.Core.Cache
private readonly Func<TEntity, TId> _entityGetId;
private readonly bool _expires;
public FullDataSetRepositoryCachePolicy(IAppPolicyCache cache, IScopeAccessor scopeAccessor, Func<TEntity, TId> entityGetId, bool expires)
public FullDataSetRepositoryCachePolicy(IRuntimeCacheProvider cache, IScopeAccessor scopeAccessor, Func<TEntity, TId> entityGetId, bool expires)
: base(cache, scopeAccessor)
{
_entityGetId = entityGetId;
@@ -55,11 +55,11 @@ namespace Umbraco.Core.Cache
if (_expires)
{
Cache.Insert(key, () => new DeepCloneableList<TEntity>(entities), TimeSpan.FromMinutes(5), true);
Cache.InsertCacheItem(key, () => new DeepCloneableList<TEntity>(entities), TimeSpan.FromMinutes(5), true);
}
else
{
Cache.Insert(key, () => new DeepCloneableList<TEntity>(entities));
Cache.InsertCacheItem(key, () => new DeepCloneableList<TEntity>(entities));
}
}
@@ -171,7 +171,7 @@ namespace Umbraco.Core.Cache
/// <inheritdoc />
public override void ClearAll()
{
Cache.Clear(GetEntityTypeCacheKey());
Cache.ClearCacheItem(GetEntityTypeCacheKey());
}
}
}
@@ -1,144 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Implements a fast <see cref="IAppCache"/> on top of HttpContext.Items.
/// </summary>
/// <remarks>
/// <para>If no current HttpContext items can be found (no current HttpContext,
/// or no Items...) then this cache acts as a pass-through and does not cache
/// anything.</para>
/// </remarks>
internal class HttpRequestAppCache : FastDictionaryAppCacheBase
{
private readonly HttpContextBase _context;
/// <summary>
/// Initializes a new instance of the <see cref="HttpRequestAppCache"/> class with a context, for unit tests!
/// </summary>
public HttpRequestAppCache(HttpContextBase context)
{
_context = context;
}
/// <summary>
/// Initializes a new instance of the <see cref="HttpRequestAppCache"/> class.
/// </summary>
/// <remarks>
/// <para>Will use HttpContext.Current.</para>
/// TODO: https://github.com/umbraco/Umbraco-CMS/issues/4239 - use IHttpContextAccessor NOT HttpContext.Current
/// </remarks>
public HttpRequestAppCache()
{ }
private IDictionary ContextItems => _context?.Items ?? HttpContext.Current?.Items;
private bool HasContextItems => _context?.Items != null || HttpContext.Current != null;
/// <inheritdoc />
public override object Get(string key, Func<object> factory)
{
//no place to cache so just return the callback result
if (HasContextItems == false) return factory();
key = GetCacheKey(key);
Lazy<object> result;
try
{
EnterWriteLock();
result = ContextItems[key] 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(factory);
ContextItems[key] = 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;
}
#region Entries
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 s && s.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;
}
#endregion
#region Lock
private const string ContextItemsLockKey = "Umbraco.Core.Cache.HttpRequestCache::LockEntered";
protected override void EnterReadLock() => EnterWriteLock();
protected override void EnterWriteLock()
{
if (HasContextItems)
{
// note: cannot keep 'entered' as a class variable here,
// since there is one per request - so storing it within
// ContextItems - which is locked, so this should be safe
var entered = false;
Monitor.Enter(ContextItems.SyncRoot, ref entered);
ContextItems[ContextItemsLockKey] = entered;
}
}
protected override void ExitReadLock() => ExitWriteLock();
protected override void ExitWriteLock()
{
var entered = (bool?) ContextItems[ContextItemsLockKey] ?? false;
if (entered)
Monitor.Exit(ContextItems.SyncRoot);
ContextItems.Remove(ContextItemsLockKey);
}
#endregion
}
}
@@ -0,0 +1,161 @@
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()
{ }
}
}
}
@@ -4,14 +4,14 @@ 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>
/// Implements <see cref="IAppPolicyCache"/> on top of a <see cref="System.Web.Caching.Cache"/>.
/// A CacheProvider that wraps the logic of the HttpRuntime.Cache
/// </summary>
/// <remarks>The underlying cache is expected to be HttpRuntime.Cache.</remarks>
internal class WebCachingAppCache : FastDictionaryAppCacheBase, IAppPolicyCache
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
@@ -21,43 +21,16 @@ namespace Umbraco.Core.Cache
private readonly System.Web.Caching.Cache _cache;
/// <summary>
/// Initializes a new instance of the <see cref="WebCachingAppCache"/> class.
/// Used for debugging
/// </summary>
public WebCachingAppCache(System.Web.Caching.Cache cache)
internal Guid InstanceId { get; private set; }
public HttpRuntimeCacheProvider(System.Web.Caching.Cache cache)
{
_cache = cache;
InstanceId = Guid.NewGuid();
}
/// <inheritdoc />
public override object Get(string key, Func<object> factory)
{
return Get(key, factory, null, dependentFiles: null);
}
/// <inheritdoc />
public object Get(string key, Func<object> factory, 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 Get(key, factory, timeout, isSliding, priority, removedCallback, dependency);
}
/// <inheritdoc />
public void Insert(string key, Func<object> factory, 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);
}
Insert(key, factory, timeout, isSliding, priority, removedCallback, dependency);
}
#region Dictionary
protected override IEnumerable<DictionaryEntry> GetDictionaryEntries()
{
const string prefix = CacheItemPrefix + "-";
@@ -75,8 +48,6 @@ namespace Umbraco.Core.Cache
return _cache.Get(key);
}
#endregion
#region Lock
protected override void EnterReadLock()
@@ -103,9 +74,33 @@ namespace Umbraco.Core.Cache
#endregion
private object Get(string key, Func<object> factory, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null)
#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)
{
key = GetCacheKey(key);
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
@@ -138,7 +133,7 @@ namespace Umbraco.Core.Cache
try
{
_locker.EnterReadLock();
result = _cache.Get(key) as Lazy<object>; // null if key not found
result = _cache.Get(cacheKey) as Lazy<object>; // null if key not found
}
finally
{
@@ -150,7 +145,7 @@ namespace Umbraco.Core.Cache
using (var lck = new UpgradeableReadLock(_locker))
{
result = _cache.Get(key) as Lazy<object>; // null if key not found
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
@@ -158,13 +153,13 @@ namespace Umbraco.Core.Cache
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
{
result = GetSafeLazy(factory);
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(key, result, dependency, absolute, sliding, priority, removedCallback);
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
}
}
@@ -180,7 +175,31 @@ namespace Umbraco.Core.Cache
return value;
}
private void Insert(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null)
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.
@@ -206,5 +225,17 @@ namespace Umbraco.Core.Cache
_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
}
}
-94
View File
@@ -1,94 +0,0 @@
using System;
using System.Collections.Generic;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Defines an application cache.
/// </summary>
public interface IAppCache
{
/// <summary>
/// Gets an item identified by its key.
/// </summary>
/// <param name="key">The key of the item.</param>
/// <returns>The item, or null if the item was not found.</returns>
object Get(string key);
/// <summary>
/// Gets or creates an item identified by its key.
/// </summary>
/// <param name="key">The key of the item.</param>
/// <param name="factory">A factory function that can create the item.</param>
/// <returns>The item.</returns>
object Get(string key, Func<object> factory);
/// <summary>
/// Gets items with a key starting with the specified value.
/// </summary>
/// <param name="keyStartsWith">The StartsWith value to use in the search.</param>
/// <returns>Items matching the search.</returns>
IEnumerable<object> SearchByKey(string keyStartsWith);
/// <summary>
/// Gets items with a key matching a regular expression.
/// </summary>
/// <param name="regex">The regular expression.</param>
/// <returns>Items matching the search.</returns>
IEnumerable<object> SearchByRegex(string regex);
/// <summary>
/// Removes all items from the cache.
/// </summary>
void Clear();
/// <summary>
/// Removes an item identified by its key from the cache.
/// </summary>
/// <param name="key">The key of the item.</param>
void Clear(string key);
/// <summary>
/// Removes items of a specified type from the cache.
/// </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 ClearOfType(string typeName);
/// <summary>
/// Removes items of a specified type from the cache.
/// </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 ClearOfType<T>();
/// <summary>
/// Removes items of a specified type from the cache.
/// </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 ClearOfType<T>(Func<string, T, bool> predicate);
/// <summary>
/// Clears items with a key starting with the specified value.
/// </summary>
/// <param name="keyStartsWith">The StartsWith value to use in the search.</param>
void ClearByKey(string keyStartsWith);
/// <summary>
/// Clears items with a key matching a regular expression.
/// </summary>
/// <param name="regex">The regular expression.</param>
void ClearByRegex(string regex);
}
}
-52
View File
@@ -1,52 +0,0 @@
using System;
using System.Web.Caching;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Defines an application cache that support cache policies.
/// </summary>
/// <remarks>A cache policy can be used to cache with timeouts,
/// or depending on files, and with a remove callback, etc.</remarks>
public interface IAppPolicyCache : IAppCache
{
/// <summary>
/// Gets an item identified by its key.
/// </summary>
/// <param name="key">The key of the item.</param>
/// <param name="factory">A factory function that can create the item.</param>
/// <param name="timeout">An optional cache timeout.</param>
/// <param name="isSliding">An optional value indicating whether the cache timeout is sliding (default is false).</param>
/// <param name="priority">An optional cache priority (default is Normal).</param>
/// <param name="removedCallback">An optional callback to handle removals.</param>
/// <param name="dependentFiles">Files the cache entry depends on.</param>
/// <returns>The item.</returns>
object Get(
string key,
Func<object> factory,
TimeSpan? timeout,
bool isSliding = false,
CacheItemPriority priority = CacheItemPriority.Normal,
CacheItemRemovedCallback removedCallback = null,
string[] dependentFiles = null);
/// <summary>
/// Inserts an item.
/// </summary>
/// <param name="key">The key of the item.</param>
/// <param name="factory">A factory function that can create the item.</param>
/// <param name="timeout">An optional cache timeout.</param>
/// <param name="isSliding">An optional value indicating whether the cache timeout is sliding (default is false).</param>
/// <param name="priority">An optional cache priority (default is Normal).</param>
/// <param name="removedCallback">An optional callback to handle removals.</param>
/// <param name="dependentFiles">Files the cache entry depends on.</param>
void Insert(
string key,
Func<object> factory,
TimeSpan? timeout = null,
bool isSliding = false,
CacheItemPriority priority = CacheItemPriority.Normal,
CacheItemRemovedCallback removedCallback = null,
string[] dependentFiles = null);
}
}
+68
View File
@@ -0,0 +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);
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ using Umbraco.Core.Composing;
namespace Umbraco.Core.Cache
{
/// <summary>
/// The IcacheRefresher Interface is used for load balancing.
/// The IcacheRefresher Interface is used for loadbalancing.
///
/// </summary>
public interface ICacheRefresher : IDiscoverable
@@ -48,7 +48,7 @@ namespace Umbraco.Core.Cache
/// Updates an entity.
/// </summary>
/// <param name="entity">The entity.</param>
/// <param name="persistUpdated">The repository PersistUpdatedItem method.</param>
/// <param name="persistUpdated">The reopsitory PersistUpdatedItem method.</param>
/// <remarks>Updates the entity in the repository, and updates the cache accordingly.</remarks>
void Update(TEntity entity, Action<TEntity> persistUpdated);
@@ -0,0 +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);
}
}
-41
View File
@@ -1,41 +0,0 @@
using System;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Represents a dictionary of <see cref="IAppPolicyCache"/> for types.
/// </summary>
/// <remarks>
/// <para>Isolated caches are used by e.g. repositories, to ensure that each cached entity
/// type has its own cache, so that lookups are fast and the repository does not need to
/// search through all keys on a global scale.</para>
/// </remarks>
public class IsolatedCaches : AppPolicedCacheDictionary<Type>
{
/// <summary>
/// Initializes a new instance of the <see cref="IsolatedCaches"/> class.
/// </summary>
/// <param name="cacheFactory"></param>
public IsolatedCaches(Func<Type, IAppPolicyCache> cacheFactory)
: base(cacheFactory)
{ }
/// <summary>
/// Gets a cache.
/// </summary>
public IAppPolicyCache GetOrCreate<T>()
=> GetOrCreate(typeof(T));
/// <summary>
/// Tries to get a cache.
/// </summary>
public Attempt<IAppPolicyCache> Get<T>()
=> Get(typeof(T));
/// <summary>
/// Clears a cache.
/// </summary>
public void ClearCache<T>()
=> ClearCache(typeof(T));
}
}
@@ -0,0 +1,91 @@
using System;
using System.Collections.Concurrent;
namespace Umbraco.Core.Cache
{
/// <summary>
/// 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
/// that the repository doesn't need to search through all keys on a global scale.
/// </remarks>
public class IsolatedRuntimeCache
{
internal Func<Type, IRuntimeCacheProvider> CacheFactory { get; set; }
/// <summary>
/// Constructor that allows specifying a factory for the type of runtime isolated cache to create
/// </summary>
/// <param name="cacheFactory"></param>
public IsolatedRuntimeCache(Func<Type, IRuntimeCacheProvider> cacheFactory)
{
CacheFactory = cacheFactory;
}
private readonly ConcurrentDictionary<Type, IRuntimeCacheProvider> _isolatedCache = new ConcurrentDictionary<Type, IRuntimeCacheProvider>();
/// <summary>
/// Returns an isolated runtime cache for a given type
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public IRuntimeCacheProvider GetOrCreateCache<T>()
{
return _isolatedCache.GetOrAdd(typeof(T), type => CacheFactory(type));
}
/// <summary>
/// Returns an isolated runtime cache for a given type
/// </summary>
/// <returns></returns>
public IRuntimeCacheProvider GetOrCreateCache(Type type)
{
return _isolatedCache.GetOrAdd(type, t => CacheFactory(t));
}
/// <summary>
/// Tries to get a cache by the type specified
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public Attempt<IRuntimeCacheProvider> GetCache<T>()
{
IRuntimeCacheProvider cache;
if (_isolatedCache.TryGetValue(typeof(T), out cache))
{
return Attempt.Succeed(cache);
}
return Attempt<IRuntimeCacheProvider>.Fail();
}
/// <summary>
/// Clears all values inside this isolated runtime cache
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public void ClearCache<T>()
{
IRuntimeCacheProvider cache;
if (_isolatedCache.TryGetValue(typeof(T), out cache))
{
cache.ClearAllCache();
}
}
/// <summary>
/// Clears all of the isolated caches
/// </summary>
public void ClearAllCaches()
{
foreach (var key in _isolatedCache.Keys)
{
IRuntimeCacheProvider cache;
if (_isolatedCache.TryRemove(key, out cache))
{
cache.ClearAllCache();
}
}
}
}
}
@@ -13,8 +13,8 @@ namespace Umbraco.Core.Cache
/// <summary>
/// Initializes a new instance of the <see cref="JsonCacheRefresherBase{TInstanceType}"/>.
/// </summary>
/// <param name="appCaches">A cache helper.</param>
protected JsonCacheRefresherBase(AppCaches appCaches) : base(appCaches)
/// <param name="cacheHelper">A cache helper.</param>
protected JsonCacheRefresherBase(CacheHelper cacheHelper) : base(cacheHelper)
{ }
/// <summary>
-82
View File
@@ -1,82 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Caching;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Implements <see cref="IAppPolicyCache"/> and do not cache.
/// </summary>
public class NoAppCache : IAppPolicyCache
{
private NoAppCache() { }
/// <summary>
/// Gets the singleton instance.
/// </summary>
public static NoAppCache Instance { get; } = new NoAppCache();
/// <inheritdoc />
public virtual object Get(string cacheKey)
{
return null;
}
/// <inheritdoc />
public virtual object Get(string cacheKey, Func<object> factory)
{
return factory();
}
/// <inheritdoc />
public virtual IEnumerable<object> SearchByKey(string keyStartsWith)
{
return Enumerable.Empty<object>();
}
/// <inheritdoc />
public IEnumerable<object> SearchByRegex(string regex)
{
return Enumerable.Empty<object>();
}
/// <inheritdoc />
public object Get(string key, Func<object> factory, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
return factory();
}
/// <inheritdoc />
public void Insert(string key, Func<object> factory, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{ }
/// <inheritdoc />
public virtual void Clear()
{ }
/// <inheritdoc />
public virtual void Clear(string key)
{ }
/// <inheritdoc />
public virtual void ClearOfType(string typeName)
{ }
/// <inheritdoc />
public virtual void ClearOfType<T>()
{ }
/// <inheritdoc />
public virtual void ClearOfType<T>(Func<string, T, bool> predicate)
{ }
/// <inheritdoc />
public virtual void ClearByKey(string keyStartsWith)
{ }
/// <inheritdoc />
public virtual void ClearByRegex(string regex)
{ }
}
}
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.Cache
{
@@ -12,6 +13,11 @@ namespace Umbraco.Core.Cache
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);
@@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Caching;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Represents a cache provider that does not cache anything.
/// </summary>
public class NullCacheProvider : IRuntimeCacheProvider
{
private NullCacheProvider() { }
public static NullCacheProvider Instance { get; } = new NullCacheProvider();
public virtual void ClearAllCache()
{ }
public virtual void ClearCacheItem(string key)
{ }
public virtual void ClearCacheObjectTypes(string typeName)
{ }
public virtual void ClearCacheObjectTypes<T>()
{ }
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
{ }
public virtual void ClearCacheByKeySearch(string keyStartsWith)
{ }
public virtual void ClearCacheByKeyExpression(string regexString)
{ }
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
{
return Enumerable.Empty<object>();
}
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
{
return Enumerable.Empty<object>();
}
public virtual object GetCacheItem(string cacheKey)
{
return default(object);
}
public virtual object GetCacheItem(string cacheKey, Func<object> getCacheItem)
{
return getCacheItem();
}
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
return getCacheItem();
}
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{ }
}
}
@@ -11,142 +11,31 @@ using CacheItemPriority = System.Web.Caching.CacheItemPriority;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Implements <see cref="IAppPolicyCache"/> on top of a <see cref="ObjectCache"/>.
/// Represents a cache provider that caches item in a <see cref="MemoryCache"/>.
/// A cache provider that wraps the logic of a System.Runtime.Caching.ObjectCache
/// </summary>
public class ObjectCacheAppCache : IAppPolicyCache
/// <remarks>The <see cref="MemoryCache"/> is created with name "in-memory". That name is
/// used to retrieve configuration options. It does not identify the memory cache, i.e.
/// each instance of this class has its own, independent, memory cache.</remarks>
public class ObjectCacheRuntimeCacheProvider : IRuntimeCacheProvider
{
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
internal ObjectCache MemoryCache;
/// <summary>
/// Initializes a new instance of the <see cref="ObjectCacheAppCache"/>.
/// Used for debugging
/// </summary>
public ObjectCacheAppCache()
internal Guid InstanceId { get; private set; }
public ObjectCacheRuntimeCacheProvider()
{
// the MemoryCache is created with name "in-memory". That name is
// used to retrieve configuration options. It does not identify the memory cache, i.e.
// each instance of this class has its own, independent, memory cache.
MemoryCache = new MemoryCache("in-memory");
InstanceId = Guid.NewGuid();
}
/// <summary>
/// Gets the internal memory cache, for tests only!
/// </summary>
internal ObjectCache MemoryCache { get; private set; }
#region Clear
/// <inheritdoc />
public object Get(string key)
{
Lazy<object> result;
try
{
_locker.EnterReadLock();
result = MemoryCache.Get(key) as Lazy<object>; // null if key not found
}
finally
{
if (_locker.IsReadLockHeld)
_locker.ExitReadLock();
}
return result == null ? null : FastDictionaryAppCacheBase.GetSafeLazyValue(result); // return exceptions as null
}
/// <inheritdoc />
public object Get(string key, Func<object> factory)
{
return Get(key, factory, null);
}
/// <inheritdoc />
public IEnumerable<object> SearchByKey(string keyStartsWith)
{
KeyValuePair<string, object>[] entries;
try
{
_locker.EnterReadLock();
entries = MemoryCache
.Where(x => x.Key.InvariantStartsWith(keyStartsWith))
.ToArray(); // evaluate while locked
}
finally
{
if (_locker.IsReadLockHeld)
_locker.ExitReadLock();
}
return entries
.Select(x => FastDictionaryAppCacheBase.GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
.Where(x => x != null) // backward compat, don't store null values in the cache
.ToList();
}
/// <inheritdoc />
public IEnumerable<object> SearchByRegex(string regex)
{
var compiled = new Regex(regex, RegexOptions.Compiled);
KeyValuePair<string, object>[] entries;
try
{
_locker.EnterReadLock();
entries = MemoryCache
.Where(x => compiled.IsMatch(x.Key))
.ToArray(); // evaluate while locked
}
finally
{
if (_locker.IsReadLockHeld)
_locker.ExitReadLock();
}
return entries
.Select(x => FastDictionaryAppCacheBase.GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
.Where(x => x != null) // backward compat, don't store null values in the cache
.ToList();
}
/// <inheritdoc />
public object Get(string key, Func<object> factory, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
// see notes in HttpRuntimeAppCache
Lazy<object> result;
using (var lck = new UpgradeableReadLock(_locker))
{
result = MemoryCache.Get(key) as Lazy<object>;
if (result == null || FastDictionaryAppCacheBase.GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
{
result = FastDictionaryAppCacheBase.GetSafeLazy(factory);
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
lck.UpgradeToWriteLock();
//NOTE: This does an add or update
MemoryCache.Set(key, result, policy);
}
}
//return result.Value;
var value = result.Value; // will not throw (safe lazy)
if (value is FastDictionaryAppCacheBase.ExceptionHolder eh) eh.Exception.Throw(); // throw once!
return value;
}
/// <inheritdoc />
public void Insert(string key, Func<object> factory, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = 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 = FastDictionaryAppCacheBase.GetSafeLazy(factory);
var value = result.Value; // force evaluation now
if (value == null) return; // do not store null values (backward compat)
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
//NOTE: This does an add or update
MemoryCache.Set(key, result, policy);
}
/// <inheritdoc />
public virtual void Clear()
public virtual void ClearAllCache()
{
try
{
@@ -161,8 +50,7 @@ namespace Umbraco.Core.Cache
}
}
/// <inheritdoc />
public virtual void Clear(string key)
public virtual void ClearCacheItem(string key)
{
try
{
@@ -177,8 +65,7 @@ namespace Umbraco.Core.Cache
}
}
/// <inheritdoc />
public virtual void ClearOfType(string typeName)
public virtual void ClearCacheObjectTypes(string typeName)
{
var type = TypeFinder.GetTypeByName(typeName);
if (type == null) return;
@@ -192,7 +79,7 @@ namespace Umbraco.Core.Cache
// x.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 = FastDictionaryAppCacheBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
var value = DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
// if T is an interface remove anything that implements that interface
// otherwise remove exact types (not inherited types)
@@ -209,13 +96,12 @@ namespace Umbraco.Core.Cache
}
}
/// <inheritdoc />
public virtual void ClearOfType<T>()
public virtual void ClearCacheObjectTypes<T>()
{
try
{
_locker.EnterWriteLock();
var typeOfT = typeof(T);
var typeOfT = typeof (T);
var isInterface = typeOfT.IsInterface;
foreach (var key in MemoryCache
.Where(x =>
@@ -223,7 +109,7 @@ namespace Umbraco.Core.Cache
// x.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 = FastDictionaryAppCacheBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
var value = DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
// if T is an interface remove anything that implements that interface
// otherwise remove exact types (not inherited types)
@@ -241,8 +127,7 @@ namespace Umbraco.Core.Cache
}
}
/// <inheritdoc />
public virtual void ClearOfType<T>(Func<string, T, bool> predicate)
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
{
try
{
@@ -255,7 +140,7 @@ namespace Umbraco.Core.Cache
// x.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 = FastDictionaryAppCacheBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
var value = DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
if (value == null) return true;
// if T is an interface remove anything that implements that interface
@@ -274,8 +159,7 @@ namespace Umbraco.Core.Cache
}
}
/// <inheritdoc />
public virtual void ClearByKey(string keyStartsWith)
public virtual void ClearCacheByKeySearch(string keyStartsWith)
{
try
{
@@ -293,16 +177,13 @@ namespace Umbraco.Core.Cache
}
}
/// <inheritdoc />
public virtual void ClearByRegex(string regex)
public virtual void ClearCacheByKeyExpression(string regexString)
{
var compiled = new Regex(regex, RegexOptions.Compiled);
try
{
_locker.EnterWriteLock();
foreach (var key in MemoryCache
.Where(x => compiled.IsMatch(x.Key))
.Where(x => Regex.IsMatch(x.Key, regexString))
.Select(x => x.Key)
.ToArray()) // ToArray required to remove
MemoryCache.Remove(key);
@@ -314,6 +195,120 @@ namespace Umbraco.Core.Cache
}
}
#endregion
#region Get
public IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
{
KeyValuePair<string, object>[] entries;
try
{
_locker.EnterReadLock();
entries = MemoryCache
.Where(x => x.Key.InvariantStartsWith(keyStartsWith))
.ToArray(); // evaluate while locked
}
finally
{
if (_locker.IsReadLockHeld)
_locker.ExitReadLock();
}
return entries
.Select(x => DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
.Where(x => x != null) // backward compat, don't store null values in the cache
.ToList();
}
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
{
KeyValuePair<string, object>[] entries;
try
{
_locker.EnterReadLock();
entries = MemoryCache
.Where(x => Regex.IsMatch(x.Key, regexString))
.ToArray(); // evaluate while locked
}
finally
{
if (_locker.IsReadLockHeld)
_locker.ExitReadLock();
}
return entries
.Select(x => DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
.Where(x => x != null) // backward compat, don't store null values in the cache
.ToList();
}
public object GetCacheItem(string cacheKey)
{
Lazy<object> result;
try
{
_locker.EnterReadLock();
result = MemoryCache.Get(cacheKey) as Lazy<object>; // null if key not found
}
finally
{
if (_locker.IsReadLockHeld)
_locker.ExitReadLock();
}
return result == null ? null : DictionaryCacheProviderBase.GetSafeLazyValue(result); // return exceptions as null
}
public object GetCacheItem(string cacheKey, Func<object> getCacheItem)
{
return GetCacheItem(cacheKey, getCacheItem, null);
}
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
// see notes in HttpRuntimeCacheProvider
Lazy<object> result;
using (var lck = new UpgradeableReadLock(_locker))
{
result = MemoryCache.Get(cacheKey) as Lazy<object>;
if (result == null || DictionaryCacheProviderBase.GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
{
result = DictionaryCacheProviderBase.GetSafeLazy(getCacheItem);
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
lck.UpgradeToWriteLock();
//NOTE: This does an add or update
MemoryCache.Set(cacheKey, result, policy);
}
}
//return result.Value;
var value = result.Value; // will not throw (safe lazy)
if (value is DictionaryCacheProviderBase.ExceptionHolder eh) eh.Exception.Throw(); // throw once!
return value;
}
#endregion
#region Insert
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = 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 = DictionaryCacheProviderBase.GetSafeLazy(getCacheItem);
var value = result.Value; // force evaluation now
if (value == null) return; // do not store null values (backward compat)
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
//NOTE: This does an add or update
MemoryCache.Set(cacheKey, result, policy);
}
#endregion
private static CacheItemPolicy GetPolicy(TimeSpan? timeout = null, bool isSliding = false, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
var absolute = isSliding ? ObjectCache.InfiniteAbsoluteExpiration : (timeout == null ? ObjectCache.InfiniteAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
@@ -15,8 +15,8 @@ namespace Umbraco.Core.Cache
/// <summary>
/// Initializes a new instance of the <see cref="PayloadCacheRefresherBase{TInstanceType, TPayload}"/>.
/// </summary>
/// <param name="appCaches">A cache helper.</param>
protected PayloadCacheRefresherBase(AppCaches appCaches) : base(appCaches)
/// <param name="cacheHelper">A cache helper.</param>
protected PayloadCacheRefresherBase(CacheHelper cacheHelper) : base(cacheHelper)
{ }
#region Json
@@ -13,16 +13,16 @@ namespace Umbraco.Core.Cache
internal abstract class RepositoryCachePolicyBase<TEntity, TId> : IRepositoryCachePolicy<TEntity, TId>
where TEntity : class, IEntity
{
private readonly IAppPolicyCache _globalCache;
private readonly IRuntimeCacheProvider _globalCache;
private readonly IScopeAccessor _scopeAccessor;
protected RepositoryCachePolicyBase(IAppPolicyCache globalCache, IScopeAccessor scopeAccessor)
protected RepositoryCachePolicyBase(IRuntimeCacheProvider globalCache, IScopeAccessor scopeAccessor)
{
_globalCache = globalCache ?? throw new ArgumentNullException(nameof(globalCache));
_scopeAccessor = scopeAccessor ?? throw new ArgumentNullException(nameof(scopeAccessor));
}
protected IAppPolicyCache Cache
protected IRuntimeCacheProvider Cache
{
get
{
@@ -32,9 +32,9 @@ namespace Umbraco.Core.Cache
case RepositoryCacheMode.Default:
return _globalCache;
case RepositoryCacheMode.Scoped:
return ambientScope.IsolatedCaches.GetOrCreate<TEntity>();
return ambientScope.IsolatedRuntimeCache.GetOrCreateCache<TEntity>();
case RepositoryCacheMode.None:
return NoAppCache.Instance;
return NullCacheProvider.Instance;
default:
throw new NotSupportedException($"Repository cache mode {ambientScope.RepositoryCacheMode} is not supported.");
}
@@ -16,7 +16,7 @@ namespace Umbraco.Core.Cache
internal class SingleItemsOnlyRepositoryCachePolicy<TEntity, TId> : DefaultRepositoryCachePolicy<TEntity, TId>
where TEntity : class, IEntity
{
public SingleItemsOnlyRepositoryCachePolicy(IAppPolicyCache cache, IScopeAccessor scopeAccessor, RepositoryCachePolicyOptions options)
public SingleItemsOnlyRepositoryCachePolicy(IRuntimeCacheProvider cache, IScopeAccessor scopeAccessor, RepositoryCachePolicyOptions options)
: base(cache, scopeAccessor, options)
{ }
@@ -0,0 +1,79 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Represents a cache provider that statically caches item in a concurrent dictionary.
/// </summary>
public class StaticCacheProvider : ICacheProvider
{
internal readonly ConcurrentDictionary<string, object> StaticCache = new ConcurrentDictionary<string, object>();
public virtual void ClearAllCache()
{
StaticCache.Clear();
}
public virtual void ClearCacheItem(string key)
{
object val;
StaticCache.TryRemove(key, out val);
}
public virtual void ClearCacheObjectTypes(string typeName)
{
StaticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType().ToString().InvariantEquals(typeName));
}
public virtual void ClearCacheObjectTypes<T>()
{
var typeOfT = typeof(T);
StaticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT);
}
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
{
var typeOfT = typeof(T);
StaticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT && predicate(kvp.Key, (T)kvp.Value));
}
public virtual void ClearCacheByKeySearch(string keyStartsWith)
{
StaticCache.RemoveAll(kvp => kvp.Key.InvariantStartsWith(keyStartsWith));
}
public virtual void ClearCacheByKeyExpression(string regexString)
{
StaticCache.RemoveAll(kvp => Regex.IsMatch(kvp.Key, regexString));
}
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
{
return (from KeyValuePair<string, object> c in StaticCache
where c.Key.InvariantStartsWith(keyStartsWith)
select c.Value).ToList();
}
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
{
return (from KeyValuePair<string, object> c in StaticCache
where Regex.IsMatch(c.Key, regexString)
select c.Value).ToList();
}
public virtual object GetCacheItem(string cacheKey)
{
var result = StaticCache[cacheKey];
return result;
}
public virtual object GetCacheItem(string cacheKey, Func<object> getCacheItem)
{
return StaticCache.GetOrAdd(cacheKey, key => getCacheItem());
}
}
}
@@ -14,9 +14,9 @@ namespace Umbraco.Core.Cache
/// <summary>
/// Initializes a new instance of the <see cref="TypedCacheRefresherBase{TInstanceType, TEntityType}"/>.
/// </summary>
/// <param name="appCaches">A cache helper.</param>
protected TypedCacheRefresherBase(AppCaches appCaches)
: base(appCaches)
/// <param name="cacheHelper">A cache helper.</param>
protected TypedCacheRefresherBase(CacheHelper cacheHelper)
: base(cacheHelper)
{ }
#region Refresher
@@ -125,18 +125,6 @@ namespace Umbraco.Core.Collections
}
public void ReplaceAll(IEnumerable<TValue> values)
{
if (values == null) throw new ArgumentNullException(nameof(values));
Clear();
foreach (var value in values)
{
Add(value);
}
}
public bool Remove(TKey key)
{
if (!Indecies.ContainsKey(key)) return false;
@@ -3,7 +3,6 @@ using System.Linq;
using System.Text;
using System.Threading;
using System.Web;
using Umbraco.Core.Composing;
using Umbraco.Core.Events;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
@@ -11,7 +10,7 @@ using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
namespace Umbraco.Core.Compose
namespace Umbraco.Core.Components
{
public sealed class AuditEventsComponent : IComponent
{
@@ -44,22 +43,21 @@ namespace Umbraco.Core.Compose
public void Terminate()
{ }
internal static IUser UnknownUser => new User { Id = Constants.Security.UnknownUserId, Name = Constants.Security.UnknownUserName, Email = "" };
private IUser CurrentPerformingUser
{
get
{
var identity = Thread.CurrentPrincipal?.GetUmbracoIdentity();
var user = identity == null ? null : _userService.GetUserById(Convert.ToInt32(identity.Id));
return user ?? UnknownUser;
return identity == null
? new User { Id = 0, Name = "SYSTEM", Email = "" }
: _userService.GetUserById(Convert.ToInt32(identity.Id));
}
}
private IUser GetPerformingUser(int userId)
{
var found = userId >= 0 ? _userService.GetUserById(userId) : null;
return found ?? UnknownUser;
return found ?? new User {Id = 0, Name = "SYSTEM", Email = ""};
}
private string PerformingIp
@@ -1,6 +1,4 @@
using Umbraco.Core.Composing;
namespace Umbraco.Core.Compose
namespace Umbraco.Core.Components
{
public sealed class AuditEventsComposer : ComponentComposer<AuditEventsComponent>, ICoreComposer
{ }
@@ -1,8 +1,9 @@
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
/// <summary>
/// Represents the collection of <see cref="IComponent"/> implementations.
@@ -23,7 +24,7 @@ namespace Umbraco.Core.Composing
{
using (_logger.DebugDuration<ComponentCollection>($"Initializing. (log components when >{LogThresholdMilliseconds}ms)", "Initialized."))
{
foreach (var component in this)
foreach (var component in this.Reverse()) // terminate components in reverse order
{
var componentType = component.GetType();
using (_logger.DebugDuration<ComponentCollection>($"Initializing {componentType.FullName}.", $"Initialized {componentType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds))
@@ -1,8 +1,9 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
/// <summary>
/// Builds a <see cref="ComponentCollection"/>.
@@ -30,7 +31,7 @@ namespace Umbraco.Core.Composing
protected override IComponent CreateItem(IFactory factory, Type itemType)
{
using (_logger.DebugDuration<ComponentCollectionBuilder>($"Creating {itemType.FullName}.", $"Created {itemType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds))
using (_logger.DebugDuration<Composers>($"Creating {itemType.FullName}.", $"Created {itemType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds))
{
return base.CreateItem(factory, itemType);
}
@@ -1,6 +1,4 @@
using Umbraco.Core.Compose;
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
/// <summary>
/// Provides a base class for composers which compose a component.
@@ -1,6 +1,6 @@
using System;
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
/// <summary>
/// Indicates that a composer requires another composer.
@@ -1,6 +1,6 @@
using System;
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
/// <summary>
/// Indicates that a component is required by another composer.
@@ -6,7 +6,7 @@ using System.Text;
using Umbraco.Core.Collections;
using Umbraco.Core.Logging;
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
// note: this class is NOT thread-safe in any ways
@@ -70,104 +70,42 @@ namespace Umbraco.Core.Composing
}
}
internal IEnumerable<Type> PrepareComposerTypes()
{
var requirements = GetRequirements();
// only for debugging, this is verbose
//_logger.Debug<Composers>(GetComposersReport(requirements));
var sortedComposerTypes = SortComposers(requirements);
// bit verbose but should help for troubleshooting
//var text = "Ordered Composers: " + Environment.NewLine + string.Join(Environment.NewLine, sortedComposerTypes) + Environment.NewLine;
_logger.Debug<Composers>("Ordered Composers: {SortedComposerTypes}", sortedComposerTypes);
return sortedComposerTypes;
}
internal Dictionary<Type, List<Type>> GetRequirements(bool throwOnMissing = true)
private IEnumerable<Type> PrepareComposerTypes()
{
// create a list, remove those that cannot be enabled due to runtime level
var composerTypeList = _composerTypes
.Where(x =>
{
// use the min/max levels specified by the attribute if any
// otherwise, min: user composers are Run, anything else is Unknown (always run)
// max: everything is Run (always run)
// use the min level specified by the attribute if any
// otherwise, user composers have Run min level, anything else is Unknown (always run)
var attr = x.GetCustomAttribute<RuntimeLevelAttribute>();
var minLevel = attr?.MinLevel ?? (x.Implements<IUserComposer>() ? RuntimeLevel.Run : RuntimeLevel.Unknown);
var maxLevel = attr?.MaxLevel ?? RuntimeLevel.Run;
return _composition.RuntimeState.Level >= minLevel && _composition.RuntimeState.Level <= maxLevel;
return _composition.RuntimeState.Level >= minLevel;
})
.ToList();
// enable or disable composers
EnableDisableComposers(composerTypeList);
void GatherInterfaces<TAttribute>(Type type, Func<TAttribute, Type> getTypeInAttribute, HashSet<Type> iset, List<Type> set2)
where TAttribute : Attribute
{
foreach (var attribute in type.GetCustomAttributes<TAttribute>())
{
var typeInAttribute = getTypeInAttribute(attribute);
if (typeInAttribute != null && // if the attribute references a type ...
typeInAttribute.IsInterface && // ... which is an interface ...
typeof(IComposer).IsAssignableFrom(typeInAttribute) && // ... which implements IComposer ...
!iset.Contains(typeInAttribute)) // ... which is not already in the list
{
// add it to the new list
iset.Add(typeInAttribute);
set2.Add(typeInAttribute);
// add all its interfaces implementing IComposer
foreach (var i in typeInAttribute.GetInterfaces().Where(x => typeof(IComposer).IsAssignableFrom(x)))
{
iset.Add(i);
set2.Add(i);
}
}
}
}
// gather interfaces too
var interfaces = new HashSet<Type>(composerTypeList.SelectMany(x => x.GetInterfaces().Where(y => typeof(IComposer).IsAssignableFrom(y))));
composerTypeList.AddRange(interfaces);
var list1 = composerTypeList;
while (list1.Count > 0)
{
var list2 = new List<Type>();
foreach (var t in list1)
{
GatherInterfaces<ComposeAfterAttribute>(t, a => a.RequiredType, interfaces, list2);
GatherInterfaces<ComposeBeforeAttribute>(t, a => a.RequiringType, interfaces, list2);
}
composerTypeList.AddRange(list2);
list1 = list2;
}
// sort the composers according to their dependencies
var requirements = new Dictionary<Type, List<Type>>();
foreach (var type in composerTypeList)
requirements[type] = null;
foreach (var type in composerTypeList) requirements[type] = null;
foreach (var type in composerTypeList)
{
GatherRequirementsFromAfterAttribute(type, composerTypeList, requirements, throwOnMissing);
GatherRequirementsFromBeforeAttribute(type, composerTypeList, requirements);
GatherRequirementsFromRequireAttribute(type, composerTypeList, requirements);
GatherRequirementsFromRequiredByAttribute(type, composerTypeList, requirements);
}
return requirements;
}
// only for debugging, this is verbose
//_logger.Debug<Composers>(GetComposersReport(requirements));
internal IEnumerable<Type> SortComposers(Dictionary<Type, List<Type>> requirements)
{
// sort composers
var graph = new TopoGraph<Type, KeyValuePair<Type, List<Type>>>(kvp => kvp.Key, kvp => kvp.Value);
graph.AddItems(requirements);
List<Type> sortedComposerTypes;
try
{
sortedComposerTypes = graph.GetSortedItems().Select(x => x.Key).Where(x => !x.IsInterface).ToList();
sortedComposerTypes = graph.GetSortedItems().Select(x => x.Key).ToList();
}
catch (Exception e)
{
@@ -177,37 +115,40 @@ namespace Umbraco.Core.Composing
throw;
}
// bit verbose but should help for troubleshooting
//var text = "Ordered Composers: " + Environment.NewLine + string.Join(Environment.NewLine, sortedComposerTypes) + Environment.NewLine;
_logger.Debug<Composers>("Ordered Composers: {SortedComposerTypes}", sortedComposerTypes);
return sortedComposerTypes;
}
internal static string GetComposersReport(Dictionary<Type, List<Type>> requirements)
private static string GetComposersReport(Dictionary<Type, List<Type>> requirements)
{
var text = new StringBuilder();
text.AppendLine("Composers & Dependencies:");
text.AppendLine(" < compose before");
text.AppendLine(" > compose after");
text.AppendLine(" : implements");
text.AppendLine(" = depends");
text.AppendLine();
bool HasReq(IEnumerable<Type> types, Type type)
=> types.Any(x => type.IsAssignableFrom(x) && !x.IsInterface);
foreach (var kvp in requirements)
{
var type = kvp.Key;
text.AppendLine(type.FullName);
foreach (var attribute in type.GetCustomAttributes<ComposeAfterAttribute>())
{
var weak = !(attribute.RequiredType.IsInterface ? attribute.Weak == false : attribute.Weak != true);
text.AppendLine(" > " + attribute.RequiredType +
(weak ? " (weak" : " (strong") + (HasReq(requirements.Keys, attribute.RequiredType) ? ", found" : ", missing") + ")");
}
text.AppendLine(" -> " + attribute.RequiredType + (attribute.Weak.HasValue
? (attribute.Weak.Value ? " (weak)" : (" (strong" + (requirements.ContainsKey(attribute.RequiredType) ? ", missing" : "") + ")"))
: ""));
foreach (var attribute in type.GetCustomAttributes<ComposeBeforeAttribute>())
text.AppendLine(" < " + attribute.RequiringType);
text.AppendLine(" -< " + attribute.RequiringType);
foreach (var i in type.GetInterfaces())
{
text.AppendLine(" : " + i.FullName);
foreach (var attribute in i.GetCustomAttributes<ComposeAfterAttribute>())
text.AppendLine(" -> " + attribute.RequiredType + (attribute.Weak.HasValue
? (attribute.Weak.Value ? " (weak)" : (" (strong" + (requirements.ContainsKey(attribute.RequiredType) ? ", missing" : "") + ")"))
: ""));
foreach (var attribute in i.GetCustomAttributes<ComposeBeforeAttribute>())
text.AppendLine(" -< " + attribute.RequiringType);
}
if (kvp.Value != null)
foreach (var t in kvp.Value)
text.AppendLine(" = " + t);
@@ -230,46 +171,27 @@ namespace Umbraco.Core.Composing
// what happens in case of conflicting remote declarations is unspecified. more
// precisely, the last declaration to be processed wins, but the order of the
// declarations depends on the type finder and is unspecified.
void UpdateEnableInfo(Type composerType, int weight2, Dictionary<Type, EnableInfo> enabled2, bool value)
{
if (enabled.TryGetValue(composerType, out var enableInfo) == false) enableInfo = enabled2[composerType] = new EnableInfo();
if (enableInfo.Weight > weight2) return;
enableInfo.Enabled = value;
enableInfo.Weight = weight2;
}
var assemblies = types.Select(x => x.Assembly).Distinct();
foreach (var assembly in assemblies)
{
foreach (var attr in assembly.GetCustomAttributes<EnableComposerAttribute>())
{
var type = attr.EnabledType;
UpdateEnableInfo(type, 2, enabled, true);
}
foreach (var attr in assembly.GetCustomAttributes<DisableComposerAttribute>())
{
var type = attr.DisabledType;
UpdateEnableInfo(type, 2, enabled, false);
}
}
foreach (var composerType in types)
{
foreach (var attr in composerType.GetCustomAttributes<EnableAttribute>())
{
var type = attr.EnabledType ?? composerType;
var weight = type == composerType ? 1 : 3;
UpdateEnableInfo(type, weight, enabled, true);
}
if (enabled.TryGetValue(type, out var enableInfo) == false) enableInfo = enabled[type] = new EnableInfo();
var weight = type == composerType ? 1 : 2;
if (enableInfo.Weight > weight) continue;
enableInfo.Enabled = true;
enableInfo.Weight = weight;
}
foreach (var attr in composerType.GetCustomAttributes<DisableAttribute>())
{
var type = attr.DisabledType ?? composerType;
var weight = type == composerType ? 1 : 3;
UpdateEnableInfo(type, weight, enabled, false);
if (enabled.TryGetValue(type, out var enableInfo) == false) enableInfo = enabled[type] = new EnableInfo();
var weight = type == composerType ? 1 : 2;
if (enableInfo.Weight > weight) continue;
enableInfo.Enabled = false;
enableInfo.Weight = weight;
}
}
@@ -278,16 +200,16 @@ namespace Umbraco.Core.Composing
types.Remove(kvp.Key);
}
private static void GatherRequirementsFromAfterAttribute(Type type, ICollection<Type> types, IDictionary<Type, List<Type>> requirements, bool throwOnMissing = true)
private static void GatherRequirementsFromRequireAttribute(Type type, ICollection<Type> types, IDictionary<Type, List<Type>> requirements)
{
// get 'require' attributes
// these attributes are *not* inherited because we want to "custom-inherit" for interfaces only
var afterAttributes = type
var requireAttributes = type
.GetInterfaces().SelectMany(x => x.GetCustomAttributes<ComposeAfterAttribute>()) // those marking interfaces
.Concat(type.GetCustomAttributes<ComposeAfterAttribute>()); // those marking the composer
// what happens in case of conflicting attributes (different strong/weak for same type) is not specified.
foreach (var attr in afterAttributes)
foreach (var attr in requireAttributes)
{
if (attr.RequiredType == type) continue; // ignore self-requirements (+ exclude in implems, below)
@@ -295,13 +217,13 @@ namespace Umbraco.Core.Composing
// unless strong, and then require at least one enabled composer implementing that interface
if (attr.RequiredType.IsInterface)
{
var implems = types.Where(x => x != type && attr.RequiredType.IsAssignableFrom(x) && !x.IsInterface).ToList();
var implems = types.Where(x => x != type && attr.RequiredType.IsAssignableFrom(x)).ToList();
if (implems.Count > 0)
{
if (requirements[type] == null) requirements[type] = new List<Type>();
requirements[type].AddRange(implems);
}
else if (attr.Weak == false && throwOnMissing) // if explicitly set to !weak, is strong, else is weak
else if (attr.Weak == false) // if explicitly set to !weak, is strong, else is weak
throw new Exception($"Broken composer dependency: {type.FullName} -> {attr.RequiredType.FullName}.");
}
// requiring a class = require that the composer is enabled
@@ -313,28 +235,28 @@ namespace Umbraco.Core.Composing
if (requirements[type] == null) requirements[type] = new List<Type>();
requirements[type].Add(attr.RequiredType);
}
else if (attr.Weak != true && throwOnMissing) // if not explicitly set to weak, is strong
else if (attr.Weak != true) // if not explicitly set to weak, is strong
throw new Exception($"Broken composer dependency: {type.FullName} -> {attr.RequiredType.FullName}.");
}
}
}
private static void GatherRequirementsFromBeforeAttribute(Type type, ICollection<Type> types, IDictionary<Type, List<Type>> requirements)
private static void GatherRequirementsFromRequiredByAttribute(Type type, ICollection<Type> types, IDictionary<Type, List<Type>> requirements)
{
// get 'required' attributes
// these attributes are *not* inherited because we want to "custom-inherit" for interfaces only
var beforeAttributes = type
var requiredAttributes = type
.GetInterfaces().SelectMany(x => x.GetCustomAttributes<ComposeBeforeAttribute>()) // those marking interfaces
.Concat(type.GetCustomAttributes<ComposeBeforeAttribute>()); // those marking the composer
foreach (var attr in beforeAttributes)
foreach (var attr in requiredAttributes)
{
if (attr.RequiringType == type) continue; // ignore self-requirements (+ exclude in implems, below)
// required by an interface = by any enabled composer implementing this that interface
if (attr.RequiringType.IsInterface)
{
var implems = types.Where(x => x != type && attr.RequiringType.IsAssignableFrom(x) && !x.IsInterface).ToList();
var implems = types.Where(x => x != type && attr.RequiringType.IsAssignableFrom(x)).ToList();
foreach (var implem in implems)
{
if (requirements[implem] == null) requirements[implem] = new List<Type>();
@@ -1,9 +1,10 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
/// <summary>
/// Represents a composition.
@@ -16,7 +17,7 @@ namespace Umbraco.Core.Composing
public class Composition : IRegister
{
private readonly Dictionary<Type, ICollectionBuilder> _builders = new Dictionary<Type, ICollectionBuilder>();
private readonly Dictionary<string, Action<IRegister>> _uniques = new Dictionary<string, Action<IRegister>>();
private readonly Dictionary<Type, Unique> _uniques = new Dictionary<Type, Unique>();
private readonly IRegister _register;
/// <summary>
@@ -82,32 +83,11 @@ namespace Umbraco.Core.Composing
/// <inheritdoc />
public void Register<TService>(Func<IFactory, TService> factory, Lifetime lifetime = Lifetime.Transient)
where TService : class
=> _register.Register(factory, lifetime);
/// <inheritdoc />
public void Register(Type serviceType, object instance)
=> _register.Register(serviceType, instance);
/// <inheritdoc />
public void RegisterFor<TService, TTarget>(Lifetime lifetime = Lifetime.Transient)
where TService : class
=> _register.RegisterFor<TService, TTarget>(lifetime);
/// <inheritdoc />
public void RegisterFor<TService, TTarget>(Type implementingType, Lifetime lifetime = Lifetime.Transient)
where TService : class
=> _register.RegisterFor<TService, TTarget>(implementingType, lifetime);
/// <inheritdoc />
public void RegisterFor<TService, TTarget>(Func<IFactory, TService> factory, Lifetime lifetime = Lifetime.Transient)
where TService : class
=> _register.RegisterFor<TService, TTarget>(factory, lifetime);
/// <inheritdoc />
public void RegisterFor<TService, TTarget>(TService instance)
where TService : class
=> _register.RegisterFor<TService, TTarget>(instance);
public void RegisterInstance(Type serviceType, object instance)
=> _register.RegisterInstance(serviceType, instance);
/// <inheritdoc />
public void RegisterAuto(Type serviceBaseType)
@@ -124,20 +104,14 @@ namespace Umbraco.Core.Composing
onCreating();
foreach (var unique in _uniques.Values)
unique(_register);
_uniques.Clear(); // no point keep them around
unique.RegisterWith(_register);
foreach (var builder in _builders.Values)
builder.RegisterWith(_register);
_builders.Clear(); // no point keep them around
Configs.RegisterWith(_register);
IFactory factory = null;
// ReSharper disable once AccessToModifiedClosure -- on purpose
_register.Register(_ => factory, Lifetime.Singleton);
factory = _register.CreateFactory();
return factory;
return _register.CreateFactory();
}
/// <summary>
@@ -149,78 +123,74 @@ namespace Umbraco.Core.Composing
#region Unique
private string GetUniqueName<TService>()
=> GetUniqueName(typeof(TService));
private string GetUniqueName(Type serviceType)
=> serviceType.FullName;
private string GetUniqueName<TService, TTarget>()
=> GetUniqueName(typeof(TService), typeof(TTarget));
private string GetUniqueName(Type serviceType, Type targetType)
=> serviceType.FullName + "::" + targetType.FullName;
/// <summary>
/// Registers a unique service as its own implementation.
/// </summary>
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
public void RegisterUnique(Type serviceType)
=> _uniques[GetUniqueName(serviceType)] = register => register.Register(serviceType, Lifetime.Singleton);
/// <summary>
/// Registers a unique service with an implementation type.
/// Registers a unique service.
/// </summary>
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
public void RegisterUnique(Type serviceType, Type implementingType)
=> _uniques[GetUniqueName(serviceType)] = register => register.Register(serviceType, implementingType, Lifetime.Singleton);
=> _uniques[serviceType] = new Unique(serviceType, implementingType);
/// <summary>
/// Registers a unique service with an implementation factory.
/// </summary>
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
public void RegisterUnique<TService>(Func<IFactory, TService> factory)
where TService : class
=> _uniques[GetUniqueName<TService>()] = register => register.Register<TService>(factory, Lifetime.Singleton);
/// <summary>
/// Registers a unique service with an implementing instance.
/// Registers a unique service.
/// </summary>
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
public void RegisterUnique(Type serviceType, object instance)
=> _uniques[GetUniqueName(serviceType)] = register => register.Register(serviceType, instance);
=> _uniques[serviceType] = new Unique(serviceType, instance);
/// <summary>
/// Registers a unique service for a target, as its own implementation.
/// Registers a unique service.
/// </summary>
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
public void RegisterUniqueFor<TService, TTarget>()
where TService : class
=> _uniques[GetUniqueName<TService, TTarget>()] = register => register.RegisterFor<TService, TTarget>(Lifetime.Singleton);
public void RegisterUnique<TService>(Func<IFactory, TService> factory)
=> _uniques[typeof(TService)] = new Unique<TService>(factory);
/// <summary>
/// Registers a unique service for a target, with an implementing type.
/// </summary>
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
public void RegisterUniqueFor<TService, TTarget>(Type implementingType)
where TService : class
=> _uniques[GetUniqueName<TService, TTarget>()] = register => register.RegisterFor<TService, TTarget>(implementingType, Lifetime.Singleton);
private class Unique
{
private readonly Type _serviceType;
private readonly Type _implementingType;
private readonly object _instance;
/// <summary>
/// Registers a unique service for a target, with an implementation factory.
/// </summary>
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
public void RegisterUniqueFor<TService, TTarget>(Func<IFactory, TService> factory)
where TService : class
=> _uniques[GetUniqueName<TService, TTarget>()] = register => register.RegisterFor<TService, TTarget>(factory, Lifetime.Singleton);
protected Unique(Type serviceType)
{
_serviceType = serviceType;
}
/// <summary>
/// Registers a unique service for a target, with an implementing instance.
/// </summary>
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
public void RegisterUniqueFor<TService, TTarget>(TService instance)
where TService : class
=> _uniques[GetUniqueName<TService, TTarget>()] = register => register.RegisterFor<TService, TTarget>(instance);
public Unique(Type serviceType, Type implementingType)
: this(serviceType)
{
_implementingType = implementingType;
}
public Unique(Type serviceType, object instance)
: this(serviceType)
{
_instance = instance;
}
public virtual void RegisterWith(IRegister register)
{
if (_implementingType != null)
register.Register(_serviceType, _implementingType, Lifetime.Singleton);
else if (_instance != null)
register.RegisterInstance(_serviceType, _instance);
}
}
private class Unique<TService> : Unique
{
private readonly Func<IFactory, TService> _factory;
public Unique(Func<IFactory, TService> factory)
: base(typeof(TService))
{
_factory = factory;
}
public override void RegisterWith(IRegister register)
{
register.Register(_factory, Lifetime.Singleton);
}
}
#endregion
@@ -1,24 +1,62 @@
using System;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Dictionary;
using Umbraco.Core.Composing;
using Umbraco.Core.IO;
using Umbraco.Core.Logging.Viewer;
using Umbraco.Core.Manifest;
using Umbraco.Core.Migrations;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PackageActions;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Strings;
using Umbraco.Core.Sync;
using Umbraco.Core._Legacy.PackageActions;
namespace Umbraco.Core
namespace Umbraco.Core.Components
{
/// <summary>
/// Provides extension methods to the <see cref="Composition"/> class.
/// </summary>
public static partial class CompositionExtensions
public static class CompositionExtensions
{
#region FileSystems
/// <summary>
/// Registers a filesystem.
/// </summary>
/// <typeparam name="TFileSystem">The type of the filesystem.</typeparam>
/// <typeparam name="TImplementing">The implementing type.</typeparam>
/// <param name="composition">The composition.</param>
/// <param name="supportingFileSystemFactory">A factory method creating the supporting filesystem.</param>
/// <returns>The register.</returns>
public static void RegisterFileSystem<TFileSystem, TImplementing>(this Composition composition, Func<IFactory, IFileSystem> supportingFileSystemFactory)
where TImplementing : FileSystemWrapper, TFileSystem
{
composition.RegisterUnique<TFileSystem>(factory =>
{
var fileSystems = factory.GetInstance<FileSystems>();
return fileSystems.GetFileSystem<TImplementing>(supportingFileSystemFactory(factory));
});
}
/// <summary>
/// Registers a filesystem.
/// </summary>
/// <typeparam name="TFileSystem">The type of the filesystem.</typeparam>
/// <param name="composition">The composition.</param>
/// <param name="supportingFileSystemFactory">A factory method creating the supporting filesystem.</param>
/// <returns>The register.</returns>
public static void RegisterFileSystem<TFileSystem>(this Composition composition, Func<IFactory, IFileSystem> supportingFileSystemFactory)
where TFileSystem : FileSystemWrapper
{
composition.RegisterUnique(factory =>
{
var fileSystems = factory.GetInstance<FileSystems>();
return fileSystems.GetFileSystem<TFileSystem>(supportingFileSystemFactory(factory));
});
}
#endregion
#region Collection Builders
/// <summary>
@@ -67,15 +105,15 @@ namespace Umbraco.Core
/// Gets the validators collection builder.
/// </summary>
/// <param name="composition">The composition.</param>
internal static ManifestValueValidatorCollectionBuilder ManifestValueValidators(this Composition composition)
internal static ManifestValueValidatorCollectionBuilder Validators(this Composition composition)
=> composition.WithCollectionBuilder<ManifestValueValidatorCollectionBuilder>();
/// <summary>
/// Gets the manifest filter collection builder.
/// Gets the post-migrations collection builder.
/// </summary>
/// <param name="composition">The composition.</param>
public static ManifestFilterCollectionBuilder ManifestFilters(this Composition composition)
=> composition.WithCollectionBuilder<ManifestFilterCollectionBuilder>();
internal static PostMigrationCollectionBuilder PostMigrations(this Composition composition)
=> composition.WithCollectionBuilder<PostMigrationCollectionBuilder>();
/// <summary>
/// Gets the components collection builder.
@@ -164,7 +202,7 @@ namespace Umbraco.Core
/// Sets the server registrar.
/// </summary>
/// <param name="composition">The composition.</param>
/// <param name="factory">A function creating a server registrar.</param>
/// <param name="factory">A function creating a server registar.</param>
public static void SetServerRegistrar(this Composition composition, Func<IFactory, IServerRegistrar> factory)
{
composition.RegisterUnique(factory);
@@ -211,28 +249,6 @@ namespace Umbraco.Core
composition.RegisterUnique(_ => registrar);
}
/// <summary>
/// Sets the database server messenger options.
/// </summary>
/// <param name="composition">The composition.</param>
/// <param name="factory">A function creating the options.</param>
/// <remarks>Use DatabaseServerRegistrarAndMessengerComposer.GetDefaultOptions to get the options that Umbraco would use by default.</remarks>
public static void SetDatabaseServerMessengerOptions(this Composition composition, Func<IFactory, DatabaseServerMessengerOptions> factory)
{
composition.RegisterUnique(factory);
}
/// <summary>
/// Sets the database server messenger options.
/// </summary>
/// <param name="composition">The composition.</param>
/// <param name="options">Options.</param>
/// <remarks>Use DatabaseServerRegistrarAndMessengerComposer.GetDefaultOptions to get the options that Umbraco would use by default.</remarks>
public static void SetDatabaseServerMessengerOptions(this Composition composition, DatabaseServerMessengerOptions options)
{
composition.RegisterUnique(_ => options);
}
/// <summary>
/// Sets the short string helper.
/// </summary>
@@ -264,53 +280,6 @@ namespace Umbraco.Core
composition.RegisterUnique(_ => helper);
}
/// <summary>
/// Sets the underlying media filesystem.
/// </summary>
/// <param name="composition">A composition.</param>
/// <param name="filesystemFactory">A filesystem factory.</param>
public static void SetMediaFileSystem(this Composition composition, Func<IFactory, IFileSystem> filesystemFactory)
=> composition.RegisterUniqueFor<IFileSystem, IMediaFileSystem>(filesystemFactory);
/// <summary>
/// Sets the underlying media filesystem.
/// </summary>
/// <param name="composition">A composition.</param>
/// <param name="filesystemFactory">A filesystem factory.</param>
public static void SetMediaFileSystem(this Composition composition, Func<IFileSystem> filesystemFactory)
=> composition.RegisterUniqueFor<IFileSystem, IMediaFileSystem>(_ => filesystemFactory());
/// <summary>
/// Sets the log viewer.
/// </summary>
/// <typeparam name="T">The type of the log viewer.</typeparam>
/// <param name="composition">The composition.</param>
public static void SetLogViewer<T>(this Composition composition)
where T : ILogViewer
{
composition.RegisterUnique<ILogViewer, T>();
}
/// <summary>
/// Sets the log viewer.
/// </summary>
/// <param name="composition">The composition.</param>
/// <param name="factory">A function creating a log viewer.</param>
public static void SetLogViewer(this Composition composition, Func<IFactory, ILogViewer> factory)
{
composition.RegisterUnique(factory);
}
/// <summary>
/// Sets the log viewer.
/// </summary>
/// <param name="composition">A composition.</param>
/// <param name="helper">A log viewer.</param>
public static void SetLogViewer(this Composition composition, ILogViewer viewer)
{
composition.RegisterUnique(_ => viewer);
}
#endregion
}
}
@@ -1,6 +1,6 @@
using System;
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
/// <summary>
/// Indicates that a composer should be disabled.
@@ -9,8 +9,9 @@ namespace Umbraco.Core.Composing
/// <para>If a type is specified, disables the composer of that type, else disables the composer marked with the attribute.</para>
/// <para>This attribute is *not* inherited.</para>
/// <para>This attribute applies to classes only, it is not possible to enable/disable interfaces.</para>
/// <para>Assembly-level <see cref="DisableComposerAttribute"/> has greater priority than <see cref="DisableAttribute"/>
/// attribute when it is marking the composer itself, but lower priority that when it is referencing another composer.</para>
/// <para>If a composer ends up being both enabled and disabled: attributes marking the composer itself have lower priority
/// than attributes on *other* composers, eg if a composer declares itself as disabled it is possible to enable it from
/// another composer. Anything else is unspecified.</para>
/// </remarks>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public class DisableAttribute : Attribute
@@ -1,6 +1,6 @@
using System;
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
/// <summary>
/// Indicates that a composer should be enabled.
@@ -9,8 +9,9 @@ namespace Umbraco.Core.Composing
/// <para>If a type is specified, enables the composer of that type, else enables the composer marked with the attribute.</para>
/// <para>This attribute is *not* inherited.</para>
/// <para>This attribute applies to classes only, it is not possible to enable/disable interfaces.</para>
/// <para>Assembly-level <see cref="DisableComposerAttribute"/> has greater priority than <see cref="DisableAttribute"/>
/// attribute when it is marking the composer itself, but lower priority that when it is referencing another composer.</para>
/// <para>If a composer ends up being both enabled and disabled: attributes marking the composer itself have lower priority
/// than attributes on *other* composers, eg if a composer declares itself as disabled it is possible to enable it from
/// another composer. Anything else is unspecified.</para>
/// </remarks>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public class EnableAttribute : Attribute
@@ -1,4 +1,4 @@
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
/// <summary>
/// Represents a component.
@@ -1,4 +1,6 @@
namespace Umbraco.Core.Composing
using Umbraco.Core.Composing;
namespace Umbraco.Core.Components
{
/// <summary>
/// Represents a composer.
@@ -8,6 +10,7 @@
/// <summary>
/// Compose.
/// </summary>
/// <param name="composition"></param>
void Compose(Composition composition);
}
}
}
@@ -0,0 +1,13 @@
namespace Umbraco.Core.Components
{
/// <summary>
/// Represents a core <see cref="IComposer"/>.
/// </summary>
/// <remarks>
/// <para>All core composers are required by (compose before) all user composers,
/// and require (compose after) all runtime composers.</para>
/// </remarks>
[ComposeAfter(typeof(IRuntimeComposer))]
public interface ICoreComposer : IComposer
{ }
}
@@ -0,0 +1,11 @@
namespace Umbraco.Core.Components
{
/// <summary>
/// Represents a runtime <see cref="IComposer"/>.
/// </summary>
/// <remarks>
/// <para>All runtime composers are required by (compose before) all core composers</para>
/// </remarks>
public interface IRuntimeComposer : IComposer
{ }
}
@@ -1,12 +1,12 @@
namespace Umbraco.Core.Composing
namespace Umbraco.Core.Components
{
/// <summary>
/// Represents a user <see cref="IComposer"/>.
/// </summary>
/// <remarks>
/// <para>User composers compose after core composers, and before the final composer.</para>
/// <para>All user composers require (compose after) all core composers.</para>
/// </remarks>
[ComposeAfter(typeof(ICoreComposer))]
public interface IUserComposer : IComposer
{ }
}
}
@@ -1,10 +1,9 @@
using System.IO;
using Umbraco.Core.Composing;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Manifest;
namespace Umbraco.Core.Compose
namespace Umbraco.Core.Components
{
public sealed class ManifestWatcherComponent : IComponent
{
@@ -1,6 +1,4 @@
using Umbraco.Core.Composing;
namespace Umbraco.Core.Compose
namespace Umbraco.Core.Components
{
[RuntimeLevel(MinLevel = RuntimeLevel.Run)]
public class ManifestWatcherComposer : ComponentComposer<ManifestWatcherComponent>, ICoreComposer
@@ -3,9 +3,9 @@ using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
namespace Umbraco.Core.Compose
namespace Umbraco.Core.Components
{
// TODO: This should just exist in the content service/repo!
//TODO: This should just exist in the content service/repo!
public sealed class RelateOnCopyComponent : IComponent
{
public void Initialize()
@@ -1,6 +1,4 @@
using Umbraco.Core.Composing;
namespace Umbraco.Core.Compose
namespace Umbraco.Core.Components
{
[RuntimeLevel(MinLevel = RuntimeLevel.Run)]
public sealed class RelateOnCopyComposer : ComponentComposer<RelateOnCopyComponent>, ICoreComposer
@@ -5,7 +5,7 @@ using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
namespace Umbraco.Core.Compose
namespace Umbraco.Core.Components
{
public sealed class RelateOnTrashComponent : IComponent
{
@@ -22,7 +22,7 @@ namespace Umbraco.Core.Compose
private static void ContentService_Moved(IContentService sender, MoveEventArgs<IContent> e)
{
foreach (var item in e.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Constants.System.RecycleBinContentString)))
foreach (var item in e.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Constants.System.RecycleBinContent.ToInvariantString())))
{
var relationService = Current.Services.RelationService;
const string relationTypeAlias = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias;
@@ -37,7 +37,7 @@ namespace Umbraco.Core.Compose
private static void MediaService_Moved(IMediaService sender, MoveEventArgs<IMedia> e)
{
foreach (var item in e.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Constants.System.RecycleBinMediaString)))
foreach (var item in e.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Constants.System.RecycleBinMedia.ToInvariantString())))
{
var relationService = Current.Services.RelationService;
const string relationTypeAlias = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias;
@@ -1,6 +1,4 @@
using Umbraco.Core.Composing;
namespace Umbraco.Core.Compose
namespace Umbraco.Core.Components
{
[RuntimeLevel(MinLevel = RuntimeLevel.Run)]
public sealed class RelateOnTrashComposer : ComponentComposer<RelateOnTrashComponent>, ICoreComposer
@@ -0,0 +1,13 @@
using System;
namespace Umbraco.Core.Components
{
[AttributeUsage(AttributeTargets.Class /*, AllowMultiple = false, Inherited = true*/)]
public class RuntimeLevelAttribute : Attribute
{
//public RuntimeLevelAttribute()
//{ }
public RuntimeLevel MinLevel { get; set; } = RuntimeLevel.Boot;
}
}
@@ -12,7 +12,7 @@ namespace Umbraco.Core.Composing
/// <typeparam name="TItem">The type of the items.</typeparam>
public abstract class CollectionBuilderBase<TBuilder, TCollection, TItem> : ICollectionBuilder<TCollection, TItem>
where TBuilder: CollectionBuilderBase<TBuilder, TCollection, TItem>
where TCollection : class, IBuilderCollection<TItem>
where TCollection : IBuilderCollection<TItem>
{
private readonly List<Type> _types = new List<Type>();
private readonly object _locker = new object();
@@ -1,11 +1,13 @@
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Components;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
namespace Umbraco.Core.Composing.CompositionExtensions
namespace Umbraco.Core.Composing.Composers
{
/// <summary>
/// Compose configurations.
/// </summary>
public static class Configuration
public static class ConfigurationComposer
{
public static Composition ComposeConfiguration(this Composition composition)
{
@@ -13,6 +15,7 @@ namespace Umbraco.Core.Composing.CompositionExtensions
// register others
composition.RegisterUnique(factory => factory.GetInstance<IUmbracoSettingsSection>().Content);
composition.RegisterUnique(factory => factory.GetInstance<IUmbracoSettingsSection>().Templates);
composition.RegisterUnique(factory => factory.GetInstance<IUmbracoSettingsSection>().RequestHandler);
composition.RegisterUnique(factory => factory.GetInstance<IUmbracoSettingsSection>().Security);
@@ -0,0 +1,16 @@
using AutoMapper;
using Umbraco.Core.Components;
using Umbraco.Core.Models.Identity;
namespace Umbraco.Core.Composing.Composers
{
public static class CoreMappingProfilesComposer
{
public static Composition ComposeCoreMappingProfiles(this Composition composition)
{
composition.Register<Profile, IdentityMapperProfile>();
return composition;
}
}
}
@@ -1,10 +1,10 @@
using Umbraco.Core.Compose;
using Umbraco.Core.Components;
using Umbraco.Core.IO;
using Umbraco.Core.IO.MediaPathSchemes;
namespace Umbraco.Core.Composing.CompositionExtensions
namespace Umbraco.Core.Composing.Composers
{
public static class FileSystems
public static class FileSystemsComposer
{
/*
* HOW TO REPLACE THE MEDIA UNDERLYING FILESYSTEM
@@ -12,9 +12,10 @@ namespace Umbraco.Core.Composing.CompositionExtensions
*
* Create a component and use it to modify the composition by adding something like:
*
* composition.RegisterUniqueFor<IFileSystem, IMediaFileSystem>(...);
* composition.Container.RegisterFileSystem<IMediaFileSystem, MediaFileSystem>(
* factory => new PhysicalFileSystem("~/somewhere"));
*
* and register whatever supporting filesystem you like.
* return whatever supporting filesystem you like.
*
*
* HOW TO IMPLEMENT MY OWN FILESYSTEM
@@ -29,15 +30,12 @@ namespace Umbraco.Core.Composing.CompositionExtensions
* { }
* }
*
* The ctor can have more parameters, that will be resolved by the container.
* The ctor can have more parameters that will be resolved by the container.
*
* Register your filesystem, in a component:
*
* composition.RegisterFileSystem<MyFileSystem>();
*
* Register the underlying filesystem:
*
* composition.RegisterUniqueFor<IFileSystem, MyFileSystem>(...);
* composition.Container.RegisterFileSystem<MyFileSystem>(
* factory => new PhysicalFileSystem("~/my"));
*
* And that's it, you can inject MyFileSystem wherever it's needed.
*
@@ -50,8 +48,8 @@ namespace Umbraco.Core.Composing.CompositionExtensions
* Make the class implement the interface, then
* register your filesystem, in a component:
*
* composition.RegisterFileSystem<IMyFileSystem, MyFileSystem>();
* composition.RegisterUniqueFor<IFileSystem, IMyFileSystem>(...);
* composition.Container.RegisterFileSystem<IMyFileSystem, MyFileSystem>(
* factory => new PhysicalFileSystem("~/my"));
*
* And that's it, you can inject IMyFileSystem wherever it's needed.
*
@@ -73,24 +71,17 @@ namespace Umbraco.Core.Composing.CompositionExtensions
// it needs to be registered (not only the interface) because it provides additional
// functionality eg for scoping, and is injected in the scope provider - whereas the
// interface is really for end-users to get access to filesystems.
composition.RegisterUnique(factory => factory.CreateInstance<IO.FileSystems>(factory));
composition.RegisterUnique(factory => factory.CreateInstance<FileSystems>(factory));
// register IFileSystems, which gives access too all filesystems
composition.RegisterUnique<IFileSystems>(factory => factory.GetInstance<IO.FileSystems>());
composition.RegisterUnique<IFileSystems>(factory => factory.GetInstance<FileSystems>());
// register the scheme for media paths
composition.RegisterUnique<IMediaPathScheme, UniqueMediaPathScheme>();
composition.RegisterUnique<IMediaPathScheme, TwoGuidsMediaPathScheme>();
// register the IMediaFileSystem implementation
composition.RegisterFileSystem<IMediaFileSystem, MediaFileSystem>();
// register the supporting filesystems provider
composition.Register(factory => new SupportingFileSystems(factory), Lifetime.Singleton);
// register the IFileSystem supporting the IMediaFileSystem
// THIS IS THE ONLY THING THAT NEEDS TO CHANGE, IN ORDER TO REPLACE THE UNDERLYING FILESYSTEM
// and, SupportingFileSystem.For<IMediaFileSystem>() returns the underlying filesystem
composition.SetMediaFileSystem(() => new PhysicalFileSystem(SystemDirectories.Media));
// register the IMediaFileSystem implementation with a supporting filesystem
composition.RegisterFileSystem<IMediaFileSystem, MediaFileSystem>(
factory => new PhysicalFileSystem("~/media"));
return composition;
}

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