Merge branch 'v9/dev' into v9/task/more-flexible-startup
# Conflicts: # src/Umbraco.Web.Common/Extensions/ApplicationBuilderExtensions.cs
This commit is contained in:
422
.github/CONTRIBUTING.md
vendored
422
.github/CONTRIBUTING.md
vendored
@@ -1,211 +1,211 @@
|
||||
# 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 judgement, 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)
|
||||
* [Ownership and copyright](#ownership-and-copyright)
|
||||
* [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 Core Contributors](#the-core-contributors-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 is open](#making-changes-after-the-pr-is-open)
|
||||
* [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 occasion 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, so we can ensure that you don't put all your hard work into something we would not be able to merge.
|
||||
|
||||
Remember, it is always worth working on an issue from the `Up for grabs` list or even asking for some feedback before you send us a PR. This way, your PR will not be closed as unwanted.
|
||||
|
||||
#### Ownership and copyright
|
||||
|
||||
It is your responsibility to make sure that you're allowed to share the code you're providing us.
|
||||
For example, you should have permission from your employer or customer to share code.
|
||||
|
||||
Similarly, if your contribution is copied or adapted from somewhere else, make sure that the license allows you to reuse that for a contribution to Umbraco-CMS.
|
||||
|
||||
If you're not sure, leave a note on your contribution and we will be happy to guide you.
|
||||
|
||||
When your contribution has been accepted, it will be [MIT licensed](https://github.com/umbraco/Umbraco-CMS/blob/v8/contrib/LICENSE.md) from that time onwards.
|
||||
|
||||
### 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)
|
||||
|
||||

|
||||
|
||||
* **Clone** - when GitHub has created your fork, you can clone it in your favorite Git tool
|
||||
|
||||

|
||||
|
||||
* **Switch to the correct branch** - switch to the `v8/contrib` branch
|
||||
* **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/contrib`, 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 - you can now make use of GitHub's draft pull request status, detailed [here](https://github.blog/2019-02-14-introducing-draft-pull-requests/)). 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.
|
||||
|
||||

|
||||
|
||||
### Pull requests
|
||||
The most successful pull requests usually look a like this:
|
||||
|
||||
* Fill in the required template (shown when starting a PR on GitHub), and link your pull request to an issue on the [issue tracker,](https://github.com/umbraco/Umbraco-CMS/issues) if applicable.
|
||||
* 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. However, the more information that you give to us, the more we have to work with when considering your contributions. Good documentation of a pull request can really speed up the time it takes to review and merge your work!
|
||||
|
||||
## 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 that you 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 if this is the case.
|
||||
|
||||
### 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 Core Contributors team
|
||||
|
||||
The Core Contributors team consists of one member of Umbraco HQ, [Sebastiaan](https://github.com/nul800sebastiaan), who gets assistance from the following community members who have comitted to volunteering their free time:
|
||||
|
||||
- [Nathan Woulfe](https://github.com/nathanwoulfe)
|
||||
- [Joe Glombek](https://github.com/glombek)
|
||||
- [Laura Weatherhead](https://github.com/lssweatherhead)
|
||||
- [Michael Latouche](https://github.com/mikecp)
|
||||
- [Owain Williams](https://github.com/OwainWilliams)
|
||||
|
||||
|
||||
These wonderful people aim to provide you with a first reply to your PR, review and test out your changes and on occasions, they might ask more questions. If they are happy with your work, they'll let Umbraco HQ know by approving the PR. Hq will have final sign-off and will check the work again before it is merged.
|
||||
|
||||
### Questions?
|
||||
|
||||
You can get in touch with [the core contributors team](#the-core-contributors-team) in multiple ways; we love open conversations and we are a friendly bunch. No question you have is stupid. Any question 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, so one of us will be on hand and ready to point you in the right direction.
|
||||
|
||||
## 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 2019 v16.3+ (with .NET Core 3.0)](https://visualstudio.microsoft.com/vs/)
|
||||
* [Node.js v10+](https://nodejs.org/en/download/)
|
||||
* npm v6.4.1+ (installed with Node.js)
|
||||
* [Git command line](https://git-scm.com/download/)
|
||||
|
||||
The easiest way to get started is to open `src\umbraco.sln` in Visual Studio 2019 (version 16.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.
|
||||
|
||||
Alternatively, you can run `build.ps1` from the Powershell command line, 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.
|
||||
|
||||

|
||||
|
||||
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 are 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
|
||||
```
|
||||
The caching for the back office has been described as 'aggressive' so we often find it's best when making back office changes to disable caching in the browser to help you to see the changes you're making.
|
||||
|
||||
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 for 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/), but 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/contrib`. If you are working on v8, this is the branch you should be targetting. For v7 contributions, please target 'v7/dev'.
|
||||
|
||||
Please note: we are no longer accepting features for v7 but will continue to merge bug fixes as and when they arise.
|
||||
|
||||

|
||||
|
||||
### Making changes after the PR is open
|
||||
|
||||
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 to 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 have 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/contrib
|
||||
```
|
||||
|
||||
In this command we're syncing with the `v8/contrib` 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))
|
||||
|
||||
### And finally
|
||||
|
||||
We welcome all kinds of contributions to this repository. If you don't feel you'd like to make code changes here, you can visit our [documentation repository](https://github.com/umbraco/UmbracoDocs) and use your experience to contribute to making the docs we have, even better. We also encourage community members to feel free to comment on others' pull requests and issues - the expertise we have is not limited to the Core Contributors and HQ. So, if you see something on the issue tracker or pull requests you feel you can add to, please don't be shy.
|
||||
# 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 judgement, 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)
|
||||
* [Ownership and copyright](#ownership-and-copyright)
|
||||
* [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 Core Contributors](#the-core-contributors-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 is open](#making-changes-after-the-pr-is-open)
|
||||
* [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 occasion 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, so we can ensure that you don't put all your hard work into something we would not be able to merge.
|
||||
|
||||
Remember, it is always worth working on an issue from the `Up for grabs` list or even asking for some feedback before you send us a PR. This way, your PR will not be closed as unwanted.
|
||||
|
||||
#### Ownership and copyright
|
||||
|
||||
It is your responsibility to make sure that you're allowed to share the code you're providing us.
|
||||
For example, you should have permission from your employer or customer to share code.
|
||||
|
||||
Similarly, if your contribution is copied or adapted from somewhere else, make sure that the license allows you to reuse that for a contribution to Umbraco-CMS.
|
||||
|
||||
If you're not sure, leave a note on your contribution and we will be happy to guide you.
|
||||
|
||||
When your contribution has been accepted, it will be [MIT licensed](https://github.com/umbraco/Umbraco-CMS/blob/v8/contrib/LICENSE.md) from that time onwards.
|
||||
|
||||
### 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)
|
||||
|
||||

|
||||
|
||||
* **Clone** - when GitHub has created your fork, you can clone it in your favorite Git tool
|
||||
|
||||

|
||||
|
||||
* **Switch to the correct branch** - switch to the `v8/contrib` branch
|
||||
* **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/contrib`, 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 - you can now make use of GitHub's draft pull request status, detailed [here](https://github.blog/2019-02-14-introducing-draft-pull-requests/)). 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.
|
||||
|
||||

|
||||
|
||||
### Pull requests
|
||||
The most successful pull requests usually look a like this:
|
||||
|
||||
* Fill in the required template (shown when starting a PR on GitHub), and link your pull request to an issue on the [issue tracker,](https://github.com/umbraco/Umbraco-CMS/issues) if applicable.
|
||||
* 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. However, the more information that you give to us, the more we have to work with when considering your contributions. Good documentation of a pull request can really speed up the time it takes to review and merge your work!
|
||||
|
||||
## 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 that you 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 if this is the case.
|
||||
|
||||
### 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 Core Contributors team
|
||||
|
||||
The Core Contributors team consists of one member of Umbraco HQ, [Sebastiaan](https://github.com/nul800sebastiaan), who gets assistance from the following community members who have comitted to volunteering their free time:
|
||||
|
||||
- [Nathan Woulfe](https://github.com/nathanwoulfe)
|
||||
- [Joe Glombek](https://github.com/glombek)
|
||||
- [Laura Weatherhead](https://github.com/lssweatherhead)
|
||||
- [Michael Latouche](https://github.com/mikecp)
|
||||
- [Owain Williams](https://github.com/OwainWilliams)
|
||||
|
||||
|
||||
These wonderful people aim to provide you with a first reply to your PR, review and test out your changes and on occasions, they might ask more questions. If they are happy with your work, they'll let Umbraco HQ know by approving the PR. Hq will have final sign-off and will check the work again before it is merged.
|
||||
|
||||
### Questions?
|
||||
|
||||
You can get in touch with [the core contributors team](#the-core-contributors-team) in multiple ways; we love open conversations and we are a friendly bunch. No question you have is stupid. Any question 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, so one of us will be on hand and ready to point you in the right direction.
|
||||
|
||||
## 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 2019 v16.3+ (with .NET Core 3.0)](https://visualstudio.microsoft.com/vs/)
|
||||
* [Node.js v10+](https://nodejs.org/en/download/)
|
||||
* npm v6.4.1+ (installed with Node.js)
|
||||
* [Git command line](https://git-scm.com/download/)
|
||||
|
||||
The easiest way to get started is to open `src\umbraco.sln` in Visual Studio 2019 (version 16.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.
|
||||
|
||||
Alternatively, you can run `build.ps1` from the Powershell command line, 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.
|
||||
|
||||

|
||||
|
||||
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 are 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
|
||||
```
|
||||
The caching for the back office has been described as 'aggressive' so we often find it's best when making back office changes to disable caching in the browser to help you to see the changes you're making.
|
||||
|
||||
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 for 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://apidocs.umbraco.com/v9/ui#/api) (to be found in `src\Umbraco.Web.UI.Client\src`)
|
||||
* [The C# application](https://apidocs.umbraco.com/v9/csharp/)
|
||||
|
||||
### Which branch should I target for my contributions?
|
||||
|
||||
We like to use [Gitflow as much as possible](https://jeffkreeftmeijer.com/git-flow/), but 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/contrib`. If you are working on v8, this is the branch you should be targetting. For v7 contributions, please target 'v7/dev'.
|
||||
|
||||
Please note: we are no longer accepting features for v7 but will continue to merge bug fixes as and when they arise.
|
||||
|
||||

|
||||
|
||||
### Making changes after the PR is open
|
||||
|
||||
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 to 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 have 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/contrib
|
||||
```
|
||||
|
||||
In this command we're syncing with the `v8/contrib` 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))
|
||||
|
||||
### And finally
|
||||
|
||||
We welcome all kinds of contributions to this repository. If you don't feel you'd like to make code changes here, you can visit our [documentation repository](https://github.com/umbraco/UmbracoDocs) and use your experience to contribute to making the docs we have, even better. We also encourage community members to feel free to comment on others' pull requests and issues - the expertise we have is not limited to the Core Contributors and HQ. So, if you see something on the issue tracker or pull requests you feel you can add to, please don't be shy.
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -207,6 +207,7 @@ src/Umbraco.Tests/TEMP/
|
||||
|
||||
src/Umbraco.Tests.UnitTests/umbraco/Data/TEMP/
|
||||
/src/Umbraco.Web.UI.NetCore/appsettings.Local.json
|
||||
src/Umbraco.Tests.Integration/DatabaseContextTests.sdf
|
||||
src/Umbraco.Tests.Integration.SqlCe/umbraco/Data/TEMP/
|
||||
src/Umbraco.Tests.Integration.SqlCe/DatabaseContextTests.sdf
|
||||
|
||||
src/Umbraco.Web.UI.NetCore/umbraco/config/appsettings-schema.json
|
||||
src/Umbraco.Web.UI.NetCore/umbraco/config/appsettings-schema.json
|
||||
|
||||
@@ -123,7 +123,7 @@ stages:
|
||||
displayName: dotnet test
|
||||
inputs:
|
||||
command: test
|
||||
projects: '**\Umbraco.Tests.Integration.csproj'
|
||||
projects: '**\Umbraco.Tests.Integration*.csproj'
|
||||
- stage: Acceptance_Tests
|
||||
displayName: Acceptance Tests
|
||||
dependsOn: []
|
||||
|
||||
@@ -503,7 +503,7 @@
|
||||
Pop-Location
|
||||
|
||||
# change baseUrl
|
||||
$BaseUrl = "https://our.umbraco.com/apidocs/v8/ui/"
|
||||
$BaseUrl = "https://apidocs.umbraco.com/v9/ui/"
|
||||
$IndexPath = "./api/index.html"
|
||||
(Get-Content $IndexPath).replace('origin + location.href.substr(origin.length).replace(rUrl, indexFile)', "`'" + $BaseUrl + "`'") | Set-Content $IndexPath
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"version": {
|
||||
"type": "parameter",
|
||||
"datatype": "string",
|
||||
"defaultValue": "9.0.0-beta003",
|
||||
"defaultValue": "9.0.0-rc001",
|
||||
"description": "The version of Umbraco to load using NuGet",
|
||||
"replaces": "UMBRACO_VERSION_FROM_TEMPLATE"
|
||||
},
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
"version": {
|
||||
"type": "parameter",
|
||||
"datatype": "string",
|
||||
"defaultValue": "9.0.0-beta004",
|
||||
"defaultValue": "9.0.0-rc001",
|
||||
"description": "The version of Umbraco to load using NuGet",
|
||||
"replaces": "UMBRACO_VERSION_FROM_TEMPLATE"
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<RootNamespace Condition="'$(name)' != '$(name{-VALUE-FORMS-}safe_namespace)'">Umbraco.Cms.Web.UI.NetCore</RootNamespace>
|
||||
@@ -15,6 +15,11 @@
|
||||
<ProjectReference Include="..\PackageTestSiteName\PackageTestSiteName.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Remove="umbraco\Data\**" />
|
||||
<Content Remove="umbraco\logs\**" />
|
||||
<Content Remove="umbraco\MediaCache\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="umbraco\Data\**" />
|
||||
<Compile Remove="umbraco\logs\**" />
|
||||
@@ -26,9 +31,6 @@
|
||||
<EmbeddedResource Remove="umbraco\MediaCache\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="umbraco\Data\**" />
|
||||
<None Remove="umbraco\logs\**" />
|
||||
<None Remove="umbraco\MediaCache\**" />
|
||||
<None Include="config\**\*.*">
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
|
||||
@@ -37,11 +39,9 @@
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Remove="umbraco\Data\**" />
|
||||
<Content Remove="umbraco\logs\**" />
|
||||
<Content Remove="umbraco\MediaCache\**" />
|
||||
<None Remove="umbraco\Data\**" />
|
||||
<None Remove="umbraco\logs\**" />
|
||||
<None Remove="umbraco\MediaCache\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Set this to true if ModelsBuilder mode is not InMemoryAuto-->
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
@using Umbraco.Web.UI.NetCore
|
||||
@using Umbraco.Extensions
|
||||
@using Umbraco.Web.PublishedModels
|
||||
@using Umbraco.Cms.Core.Models.PublishedContent
|
||||
@using Microsoft.AspNetCore.Html
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<PropertyGroup>
|
||||
<Version>9.0.0</Version>
|
||||
<AssemblyVersion>9.0.0</AssemblyVersion>
|
||||
<InformationalVersion>9.0.0-rc001.pre001</InformationalVersion>
|
||||
<InformationalVersion>9.0.0-rc001</InformationalVersion>
|
||||
<FileVersion>9.0.0</FileVersion>
|
||||
<LangVersion Condition="'$(LangVersion)' == ''">9.0</LangVersion>
|
||||
<NeutralLanguage>en-US</NeutralLanguage>
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace JsonSchema
|
||||
public UnattendedSettings Unattended { get; set; }
|
||||
public RichTextEditorSettings RichTextEditor { get; set; }
|
||||
public RuntimeMinificationSettings RuntimeMinification { get; set; }
|
||||
public BasicAuthSettings BasicAuth { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommandLineParser" Version="2.8.0" />
|
||||
<PackageReference Include="NJsonSchema" Version="10.4.4" />
|
||||
<PackageReference Include="NJsonSchema" Version="10.5.2" />
|
||||
<PackageReference Include="System.Xml.XPath.XmlDocument" Version="4.3.0" />
|
||||
<PackageReference Include="Umbraco.Forms.Core" Version="9.0.0-preview20210624.66481" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -11,9 +11,10 @@ namespace Umbraco.Extensions
|
||||
/// </summary>
|
||||
/// <param name="builder">The builder.</param>
|
||||
/// <param name="factory">A function creating a published snapshot service.</param>
|
||||
public static void SetPublishedSnapshotService(this IUmbracoBuilder builder, Func<IServiceProvider, IPublishedSnapshotService> factory)
|
||||
public static IUmbracoBuilder SetPublishedSnapshotService(this IUmbracoBuilder builder, Func<IServiceProvider, IPublishedSnapshotService> factory)
|
||||
{
|
||||
builder.Services.AddUnique(factory);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -21,10 +22,11 @@ namespace Umbraco.Extensions
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the published snapshot service.</typeparam>
|
||||
/// <param name="builder">The builder.</param>
|
||||
public static void SetPublishedSnapshotService<T>(this IUmbracoBuilder builder)
|
||||
public static IUmbracoBuilder SetPublishedSnapshotService<T>(this IUmbracoBuilder builder)
|
||||
where T : class, IPublishedSnapshotService
|
||||
{
|
||||
builder.Services.AddUnique<IPublishedSnapshotService, T>();
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -32,9 +34,10 @@ namespace Umbraco.Extensions
|
||||
/// </summary>
|
||||
/// <param name="builder">The builder.</param>
|
||||
/// <param name="service">A published snapshot service.</param>
|
||||
public static void SetPublishedSnapshotService(this IUmbracoBuilder builder, IPublishedSnapshotService service)
|
||||
public static IUmbracoBuilder SetPublishedSnapshotService(this IUmbracoBuilder builder, IPublishedSnapshotService service)
|
||||
{
|
||||
builder.Services.AddUnique(service);
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +146,7 @@ namespace Umbraco.Cms.Core.Composing
|
||||
"HtmlDiff,",
|
||||
"ICSharpCode.",
|
||||
"Iesi.Collections,", // used by NHibernate
|
||||
"JetBrains.Annotations,",
|
||||
"LightInject.", // DI
|
||||
"LightInject,",
|
||||
"Lucene.",
|
||||
|
||||
@@ -17,48 +17,57 @@ namespace Umbraco.Cms.Core.Configuration
|
||||
public string ProviderName { get; }
|
||||
public string Name { get; }
|
||||
|
||||
private string ParseProvider(string connectionString)
|
||||
private static bool IsSqlCe(DbConnectionStringBuilder builder) => (builder.TryGetValue("Data Source", out var ds)
|
||||
|| builder.TryGetValue("DataSource", out ds)) &&
|
||||
ds is string dataSource &&
|
||||
dataSource.EndsWith(".sdf");
|
||||
|
||||
private static bool IsSqlServer(DbConnectionStringBuilder builder) =>
|
||||
!string.IsNullOrEmpty(GetServer(builder)) &&
|
||||
((builder.TryGetValue("Database", out var db) && db is string database &&
|
||||
!string.IsNullOrEmpty(database)) ||
|
||||
(builder.TryGetValue("AttachDbFileName", out var a) && a is string attachDbFileName &&
|
||||
!string.IsNullOrEmpty(attachDbFileName)) ||
|
||||
(builder.TryGetValue("Initial Catalog", out var i) && i is string initialCatalog &&
|
||||
!string.IsNullOrEmpty(initialCatalog)));
|
||||
|
||||
private static string GetServer(DbConnectionStringBuilder builder)
|
||||
{
|
||||
if(builder.TryGetValue("Server", out var s) && s is string server)
|
||||
{
|
||||
return server;
|
||||
}
|
||||
|
||||
if ((builder.TryGetValue("Data Source", out var ds)
|
||||
|| builder.TryGetValue("DataSource", out ds)) && ds is string dataSource)
|
||||
{
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
private static string ParseProvider(string connectionString)
|
||||
{
|
||||
if (string.IsNullOrEmpty(connectionString))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var builder = new DbConnectionStringBuilder
|
||||
var builder = new DbConnectionStringBuilder {ConnectionString = connectionString};
|
||||
if (IsSqlCe(builder))
|
||||
{
|
||||
ConnectionString = connectionString
|
||||
};
|
||||
|
||||
if (
|
||||
(builder.TryGetValue("Data Source", out var ds)
|
||||
|| builder.TryGetValue("DataSource", out ds)) && ds is string dataSource)
|
||||
{
|
||||
if (dataSource.EndsWith(".sdf"))
|
||||
{
|
||||
return Umbraco.Cms.Core.Constants.DbProviderNames.SqlCe;
|
||||
}
|
||||
return Constants.DbProviderNames.SqlCe;
|
||||
}
|
||||
|
||||
|
||||
if (builder.TryGetValue("Server", out var s) && s is string server && !string.IsNullOrEmpty(server))
|
||||
if (IsSqlServer(builder))
|
||||
{
|
||||
if (builder.TryGetValue("Database", out var db) && db is string database && !string.IsNullOrEmpty(database))
|
||||
{
|
||||
return Umbraco.Cms.Core.Constants.DbProviderNames.SqlServer;
|
||||
}
|
||||
|
||||
if (builder.TryGetValue("AttachDbFileName", out var a) && a is string attachDbFileName && !string.IsNullOrEmpty(attachDbFileName))
|
||||
{
|
||||
return Umbraco.Cms.Core.Constants.DbProviderNames.SqlServer;
|
||||
}
|
||||
|
||||
if (builder.TryGetValue("Initial Catalog", out var i) && i is string initialCatalog && !string.IsNullOrEmpty(initialCatalog))
|
||||
{
|
||||
return Umbraco.Cms.Core.Constants.DbProviderNames.SqlServer;
|
||||
}
|
||||
return Constants.DbProviderNames.SqlServer;
|
||||
}
|
||||
|
||||
throw new ArgumentException("Cannot determine provider name from connection string", nameof(connectionString));
|
||||
throw new ArgumentException("Cannot determine provider name from connection string",
|
||||
nameof(connectionString));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
|
||||
namespace Umbraco.Core.Dashboards
|
||||
namespace Umbraco.Cms.Core.Configuration
|
||||
{
|
||||
public class ContentDashboardSettings
|
||||
{
|
||||
private const string DefaultContentDashboardPath = "cms";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the content dashboard should be available to all users.
|
||||
/// </summary>
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace Umbraco.Cms.Core.Configuration.Grid
|
||||
}
|
||||
|
||||
// add manifest editors, skip duplicates
|
||||
foreach (var gridEditor in _manifestParser.Manifest.GridEditors)
|
||||
foreach (var gridEditor in _manifestParser.CombinedManifest.GridEditors)
|
||||
{
|
||||
if (editors.Contains(gridEditor) == false) editors.Add(gridEditor);
|
||||
}
|
||||
|
||||
27
src/Umbraco.Core/Configuration/Models/BasicAuthSettings.cs
Normal file
27
src/Umbraco.Core/Configuration/Models/BasicAuthSettings.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Net;
|
||||
|
||||
namespace Umbraco.Cms.Core.Configuration.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Typed configuration options for basic authentication settings.
|
||||
/// </summary>
|
||||
[UmbracoOptions(Constants.Configuration.ConfigBasicAuth)]
|
||||
public class BasicAuthSettings
|
||||
{
|
||||
private const bool StaticEnabled = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to keep the user logged in.
|
||||
/// </summary>
|
||||
[DefaultValue(StaticEnabled)]
|
||||
public bool Enabled { get; set; } = StaticEnabled;
|
||||
|
||||
|
||||
public string[] AllowedIPs { get; set; } = Array.Empty<string>();
|
||||
}
|
||||
}
|
||||
@@ -30,5 +30,7 @@ namespace Umbraco.Cms.Core.Configuration.Models
|
||||
/// </summary>
|
||||
[DefaultValue(StaticSqlPageSize)]
|
||||
public int SqlPageSize { get; set; } = StaticSqlPageSize;
|
||||
|
||||
public bool UnPublishedContentCompression { get; set; } = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace Umbraco.Cms.Core.Configuration.Models
|
||||
internal const bool StaticHideDisabledUsersInBackOffice = false;
|
||||
internal const bool StaticAllowPasswordReset = true;
|
||||
internal const string StaticAuthCookieName = "UMB_UCONTEXT";
|
||||
internal const string StaticAllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+\\";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to keep the user logged in.
|
||||
@@ -50,6 +51,12 @@ namespace Umbraco.Cms.Core.Configuration.Models
|
||||
/// </summary>
|
||||
public bool UsernameIsEmail { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the set of allowed characters for a username
|
||||
/// </summary>
|
||||
[DefaultValue(StaticAllowedUserNameCharacters)]
|
||||
public string AllowedUserNameCharacters { get; set; } = StaticAllowedUserNameCharacters;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value for the user password settings.
|
||||
/// </summary>
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
public const string ConfigRuntimeMinification = ConfigPrefix + "RuntimeMinification";
|
||||
public const string ConfigRuntimeMinificationVersion = ConfigRuntimeMinification + ":Version";
|
||||
public const string ConfigSecurity = ConfigPrefix + "Security";
|
||||
public const string ConfigBasicAuth = ConfigPrefix + "BasicAuth";
|
||||
public const string ConfigTours = ConfigPrefix + "Tours";
|
||||
public const string ConfigTypeFinder = ConfigPrefix + "TypeFinder";
|
||||
public const string ConfigWebRouting = ConfigPrefix + "WebRouting";
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Umbraco.Cms.Core.ContentApps
|
||||
// its dependencies too, and that can create cycles or other oddities
|
||||
var manifestParser = factory.GetRequiredService<IManifestParser>();
|
||||
var ioHelper = factory.GetRequiredService<IIOHelper>();
|
||||
return base.CreateItems(factory).Concat(manifestParser.Manifest.ContentApps.Select(x => new ManifestContentAppFactory(x, ioHelper)));
|
||||
return base.CreateItems(factory).Concat(manifestParser.CombinedManifest.ContentApps.Select(x => new ManifestContentAppFactory(x, ioHelper)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
@@ -19,7 +19,7 @@ namespace Umbraco.Cms.Core.Dashboards
|
||||
// its dependencies too, and that can create cycles or other oddities
|
||||
var manifestParser = factory.GetRequiredService<IManifestParser>();
|
||||
|
||||
var dashboardSections = Merge(base.CreateItems(factory), manifestParser.Manifest.Dashboards);
|
||||
var dashboardSections = Merge(base.CreateItems(factory), manifestParser.CombinedManifest.Dashboards);
|
||||
|
||||
return dashboardSections;
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ namespace Umbraco.Cms.Core.DependencyInjection
|
||||
.AddUmbracoOptions<UmbracoPluginSettings>()
|
||||
.AddUmbracoOptions<UnattendedSettings>()
|
||||
.AddUmbracoOptions<RichTextEditorSettings>()
|
||||
.AddUmbracoOptions<BasicAuthSettings>()
|
||||
.AddUmbracoOptions<RuntimeMinificationSettings>();
|
||||
|
||||
return builder;
|
||||
|
||||
@@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Configuration;
|
||||
using Umbraco.Cms.Core.Install.Models;
|
||||
using Umbraco.Cms.Core.Semver;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
using Umbraco.Extensions;
|
||||
namespace Umbraco.Cms.Core.Install.InstallSteps
|
||||
{
|
||||
/// <summary>
|
||||
@@ -39,7 +39,7 @@ namespace Umbraco.Cms.Core.Install.InstallSteps
|
||||
|
||||
var currentState = FormatGuidState(_runtimeState.CurrentMigrationState);
|
||||
var newState = FormatGuidState(_runtimeState.FinalMigrationState);
|
||||
var newVersion = _umbracoVersion.SemanticVersion.ToString();
|
||||
var newVersion = _umbracoVersion.SemanticVersion.ToSemanticStringWithoutBuild();
|
||||
var oldVersion = new SemVersion(_umbracoVersion.SemanticVersion.Major, 0, 0).ToString(); //TODO can we find the old version somehow? e.g. from current state
|
||||
|
||||
var reportUrl = $"https://our.umbraco.com/contribute/releases/compare?from={oldVersion}&to={newVersion}¬es=1";
|
||||
|
||||
26
src/Umbraco.Core/Manifest/BundleOptions.cs
Normal file
26
src/Umbraco.Core/Manifest/BundleOptions.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
namespace Umbraco.Cms.Core.Manifest
|
||||
{
|
||||
public enum BundleOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// The default bundling behavior for assets in the package folder.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The assets will be bundled with the typical packages bundle.
|
||||
/// </remarks>
|
||||
Default = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The assets in the package will not be processed at all and will all be requested as individual assets.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This will essentially be a bundle that has composite processing turned off for both debug and production.
|
||||
/// </remarks>
|
||||
None = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The packages assets will be processed as it's own separate bundle. (in debug, files will not be processed)
|
||||
/// </summary>
|
||||
Independent = 2
|
||||
}
|
||||
}
|
||||
67
src/Umbraco.Core/Manifest/CompositePackageManifest.cs
Normal file
67
src/Umbraco.Core/Manifest/CompositePackageManifest.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Cms.Core.Manifest
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// A package manifest made up of all combined manifests
|
||||
/// </summary>
|
||||
public class CompositePackageManifest
|
||||
{
|
||||
public CompositePackageManifest(
|
||||
IReadOnlyList<IDataEditor> propertyEditors,
|
||||
IReadOnlyList<IDataEditor> parameterEditors,
|
||||
IReadOnlyList<GridEditor> gridEditors,
|
||||
IReadOnlyList<ManifestContentAppDefinition> contentApps,
|
||||
IReadOnlyList<ManifestDashboard> dashboards,
|
||||
IReadOnlyList<ManifestSection> sections,
|
||||
IReadOnlyDictionary<BundleOptions, IReadOnlyList<ManifestAssets>> scripts,
|
||||
IReadOnlyDictionary<BundleOptions, IReadOnlyList<ManifestAssets>> stylesheets)
|
||||
{
|
||||
PropertyEditors = propertyEditors ?? throw new ArgumentNullException(nameof(propertyEditors));
|
||||
ParameterEditors = parameterEditors ?? throw new ArgumentNullException(nameof(parameterEditors));
|
||||
GridEditors = gridEditors ?? throw new ArgumentNullException(nameof(gridEditors));
|
||||
ContentApps = contentApps ?? throw new ArgumentNullException(nameof(contentApps));
|
||||
Dashboards = dashboards ?? throw new ArgumentNullException(nameof(dashboards));
|
||||
Sections = sections ?? throw new ArgumentNullException(nameof(sections));
|
||||
Scripts = scripts ?? throw new ArgumentNullException(nameof(scripts));
|
||||
Stylesheets = stylesheets ?? throw new ArgumentNullException(nameof(stylesheets));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the property editors listed in the manifest.
|
||||
/// </summary>
|
||||
public IReadOnlyList<IDataEditor> PropertyEditors { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parameter editors listed in the manifest.
|
||||
/// </summary>
|
||||
public IReadOnlyList<IDataEditor> ParameterEditors { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the grid editors listed in the manifest.
|
||||
/// </summary>
|
||||
public IReadOnlyList<GridEditor> GridEditors { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content apps listed in the manifest.
|
||||
/// </summary>
|
||||
public IReadOnlyList<ManifestContentAppDefinition> ContentApps { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the dashboards listed in the manifest.
|
||||
/// </summary>
|
||||
public IReadOnlyList<ManifestDashboard> Dashboards { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sections listed in the manifest.
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<ManifestSection> Sections { get; }
|
||||
|
||||
public IReadOnlyDictionary<BundleOptions, IReadOnlyList<ManifestAssets>> Scripts { get; }
|
||||
|
||||
public IReadOnlyDictionary<BundleOptions, IReadOnlyList<ManifestAssets>> Stylesheets { get; }
|
||||
}
|
||||
}
|
||||
@@ -4,13 +4,13 @@ namespace Umbraco.Cms.Core.Manifest
|
||||
{
|
||||
public interface IManifestParser
|
||||
{
|
||||
string Path { get; set; }
|
||||
string AppPluginsPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets all manifests, merged into a single manifest object.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
PackageManifest Manifest { get; }
|
||||
CompositePackageManifest CombinedManifest { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Parses a manifest.
|
||||
|
||||
17
src/Umbraco.Core/Manifest/ManifestAssets.cs
Normal file
17
src/Umbraco.Core/Manifest/ManifestAssets.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Cms.Core.Manifest
|
||||
{
|
||||
public class ManifestAssets
|
||||
{
|
||||
public ManifestAssets(string packageName, IReadOnlyList<string> assets)
|
||||
{
|
||||
PackageName = packageName ?? throw new ArgumentNullException(nameof(packageName));
|
||||
Assets = assets ?? throw new ArgumentNullException(nameof(assets));
|
||||
}
|
||||
|
||||
public string PackageName { get; }
|
||||
public IReadOnlyList<string> Assets { get; }
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Manifest
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Represents the content of a package manifest.
|
||||
/// </summary>
|
||||
@@ -47,6 +48,8 @@ namespace Umbraco.Cms.Core.Manifest
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
public string Source { get; set; }
|
||||
[DataMember(Name = "bundleOptions")]
|
||||
public BundleOptions BundleOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the scripts listed in the manifest.
|
||||
|
||||
27
src/Umbraco.Core/Models/ContentEditing/ContentTypesByKeys.cs
Normal file
27
src/Umbraco.Core/Models/ContentEditing/ContentTypesByKeys.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Core.Models.ContentEditing
|
||||
{
|
||||
/// <summary>
|
||||
/// A model for retrieving multiple content types based on their keys.
|
||||
/// </summary>
|
||||
[DataContract(Name = "contentTypes", Namespace = "")]
|
||||
public class ContentTypesByKeys
|
||||
{
|
||||
/// <summary>
|
||||
/// ID of the parent of the content type.
|
||||
/// </summary>
|
||||
[DataMember(Name = "parentId")]
|
||||
[Required]
|
||||
public int ParentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The id of every content type to get.
|
||||
/// </summary>
|
||||
[DataMember(Name = "contentTypeKeys")]
|
||||
[Required]
|
||||
public Guid[] ContentTypeKeys { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -7,5 +7,15 @@ namespace Umbraco.Cms.Core.Notifications
|
||||
public SendEmailNotification(NotificationEmailModel message) => Message = message;
|
||||
|
||||
public NotificationEmailModel Message { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Call to tell Umbraco that the email sending is handled.
|
||||
/// </summary>
|
||||
public void HandleEmail() => IsHandled = true;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the email sending is handled.
|
||||
/// </summary>
|
||||
public bool IsHandled { get; private set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,12 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
///
|
||||
/// </remarks>
|
||||
public interface IPropertyCacheCompression
|
||||
{
|
||||
bool IsCompressed(IReadOnlyContentBase content, string propertyTypeAlias);
|
||||
{/// <summary>
|
||||
/// Whether a property on the content is/should be compressed
|
||||
/// </summary>
|
||||
/// <param name="content">The content</param>
|
||||
/// <param name="propertyTypeAlias">The property to compress or not</param>
|
||||
/// <param name="published">Whether this content is the published version</param>
|
||||
bool IsCompressed(IReadOnlyContentBase content, string propertyTypeAlias, bool published);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,13 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
{
|
||||
public interface IPropertyCacheCompressionOptions
|
||||
{
|
||||
bool IsCompressed(IReadOnlyContentBase content, IPropertyType propertyType, IDataEditor dataEditor);
|
||||
/// <summary>
|
||||
/// Whether a property on the content is/should be compressed
|
||||
/// </summary>
|
||||
/// <param name="content">The content</param>
|
||||
/// <param name="propertyType">The property to compress or not</param>
|
||||
/// <param name="dataEditor">The datatype of the property to compress or not</param>
|
||||
/// <param name="published">Whether this content is the published version</param>
|
||||
bool IsCompressed(IReadOnlyContentBase content, IPropertyType propertyType, IDataEditor dataEditor, bool published);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,14 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
|
||||
public bool TryGetMediaPath(string propertyEditorAlias, object value, out string mediaPath)
|
||||
{
|
||||
// We can't get a media path from a null value
|
||||
// The value will be null when uploading a brand new image, since we try to get the "old path" which doesn't exist yet.
|
||||
if (value is null)
|
||||
{
|
||||
mediaPath = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach(IMediaUrlGenerator generator in this)
|
||||
{
|
||||
if (generator.TryGetMediaPath(propertyEditorAlias, value, out var mp))
|
||||
|
||||
@@ -7,6 +7,6 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
/// </summary>
|
||||
public sealed class NoopPropertyCacheCompressionOptions : IPropertyCacheCompressionOptions
|
||||
{
|
||||
public bool IsCompressed(IReadOnlyContentBase content, IPropertyType propertyType, IDataEditor dataEditor) => false;
|
||||
public bool IsCompressed(IReadOnlyContentBase content, IPropertyType propertyType, IDataEditor dataEditor, bool published) => false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
public ParameterEditorCollection(DataEditorCollection dataEditors, IManifestParser manifestParser)
|
||||
: base(() => dataEditors
|
||||
.Where(x => (x.Type & EditorType.MacroParameter) > 0)
|
||||
.Union(manifestParser.Manifest.PropertyEditors))
|
||||
.Union(manifestParser.CombinedManifest.PropertyEditors))
|
||||
{ }
|
||||
|
||||
// note: virtual so it can be mocked
|
||||
|
||||
@@ -5,7 +5,7 @@ using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
|
||||
|
||||
namespace Umbraco.Core.PropertyEditors
|
||||
namespace Umbraco.Cms.Core.PropertyEditors
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
@@ -16,13 +16,13 @@ namespace Umbraco.Core.PropertyEditors
|
||||
private readonly IPropertyCacheCompressionOptions _compressionOptions;
|
||||
private readonly IReadOnlyDictionary<int, IContentTypeComposition> _contentTypes;
|
||||
private readonly PropertyEditorCollection _propertyEditors;
|
||||
private readonly ConcurrentDictionary<(int contentTypeId, string propertyAlias), bool> _isCompressedCache;
|
||||
private readonly ConcurrentDictionary<(int contentTypeId, string propertyAlias, bool published), bool> _isCompressedCache;
|
||||
|
||||
public PropertyCacheCompression(
|
||||
IPropertyCacheCompressionOptions compressionOptions,
|
||||
IReadOnlyDictionary<int, IContentTypeComposition> contentTypes,
|
||||
PropertyEditorCollection propertyEditors,
|
||||
ConcurrentDictionary<(int, string), bool> compressedStoragePropertyEditorCache)
|
||||
ConcurrentDictionary<(int, string, bool), bool> compressedStoragePropertyEditorCache)
|
||||
{
|
||||
_compressionOptions = compressionOptions;
|
||||
_contentTypes = contentTypes ?? throw new System.ArgumentNullException(nameof(contentTypes));
|
||||
@@ -30,9 +30,9 @@ namespace Umbraco.Core.PropertyEditors
|
||||
_isCompressedCache = compressedStoragePropertyEditorCache;
|
||||
}
|
||||
|
||||
public bool IsCompressed(IReadOnlyContentBase content, string alias)
|
||||
public bool IsCompressed(IReadOnlyContentBase content, string alias, bool published)
|
||||
{
|
||||
var compressedStorage = _isCompressedCache.GetOrAdd((content.ContentTypeId, alias), x =>
|
||||
var compressedStorage = _isCompressedCache.GetOrAdd((content.ContentTypeId, alias, published), x =>
|
||||
{
|
||||
if (!_contentTypes.TryGetValue(x.contentTypeId, out var ct))
|
||||
return false;
|
||||
@@ -44,7 +44,7 @@ namespace Umbraco.Core.PropertyEditors
|
||||
if (!_propertyEditors.TryGet(propertyType.PropertyEditorAlias, out var propertyEditor))
|
||||
return false;
|
||||
|
||||
return _compressionOptions.IsCompressed(content, propertyType, propertyEditor);
|
||||
return _compressionOptions.IsCompressed(content, propertyType, propertyEditor, published);
|
||||
});
|
||||
|
||||
return compressedStorage;
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
public PropertyEditorCollection(DataEditorCollection dataEditors, IManifestParser manifestParser)
|
||||
: base(() => dataEditors
|
||||
.Where(x => (x.Type & EditorType.PropertyValue) > 0)
|
||||
.Union(manifestParser.Manifest.PropertyEditors))
|
||||
.Union(manifestParser.CombinedManifest.PropertyEditors))
|
||||
{ }
|
||||
|
||||
public PropertyEditorCollection(DataEditorCollection dataEditors)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Core.PropertyEditors
|
||||
{
|
||||
/// <summary>
|
||||
/// Compress large, non published text properties
|
||||
/// </summary>
|
||||
public class UnPublishedContentPropertyCacheCompressionOptions : IPropertyCacheCompressionOptions
|
||||
{
|
||||
public bool IsCompressed(IReadOnlyContentBase content, IPropertyType propertyType, IDataEditor dataEditor, bool published)
|
||||
{
|
||||
if (!published && propertyType.SupportsPublishing && propertyType.ValueStorageType == ValueStorageType.Ntext)
|
||||
{
|
||||
//Only compress non published content that supports publishing and the property is text
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ namespace Umbraco.Cms.Core.Sections
|
||||
// its dependencies too, and that can create cycles or other oddities
|
||||
var manifestParser = factory.GetRequiredService<IManifestParser>();
|
||||
|
||||
return base.CreateItems(factory).Concat(manifestParser.Manifest.Sections);
|
||||
return base.CreateItems(factory).Concat(manifestParser.CombinedManifest.Sections);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
src/Umbraco.Core/Services/IBasicAuthService.cs
Normal file
10
src/Umbraco.Core/Services/IBasicAuthService.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Net;
|
||||
|
||||
namespace Umbraco.Cms.Core.Services
|
||||
{
|
||||
public interface IBasicAuthService
|
||||
{
|
||||
bool IsBasicAuthEnabled();
|
||||
bool IsIpAllowListed(IPAddress clientIpAddress);
|
||||
}
|
||||
}
|
||||
@@ -34,17 +34,6 @@ namespace Umbraco.Extensions
|
||||
public static string Localize(this ILocalizedTextService manager, string area, string alias, string[] tokens)
|
||||
=> manager.Localize(area, alias, Thread.CurrentThread.CurrentUICulture, ConvertToDictionaryVars(tokens));
|
||||
|
||||
/// <summary>
|
||||
/// Localize using the current thread culture
|
||||
/// </summary>
|
||||
/// <param name="manager"></param>
|
||||
/// <param name="area"></param>
|
||||
/// <param name="alias"></param>
|
||||
/// <param name="tokens"></param>
|
||||
/// <returns></returns>
|
||||
public static string Localize(this ILocalizedTextService manager, string area, string alias, IDictionary<string, string> tokens = null)
|
||||
=> manager.Localize(area, alias, Thread.CurrentThread.CurrentUICulture, tokens);
|
||||
|
||||
/// <summary>
|
||||
/// Localize a key without any variables
|
||||
/// </summary>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="5.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="5.0.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" />
|
||||
@@ -59,4 +59,8 @@
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="EmbeddedResources\**\*" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="ContentEditing" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
44
src/Umbraco.Core/WebAssets/BundlingOptions.cs
Normal file
44
src/Umbraco.Core/WebAssets/BundlingOptions.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Cms.Core.WebAssets
|
||||
{
|
||||
public struct BundlingOptions : IEquatable<BundlingOptions>
|
||||
{
|
||||
public static BundlingOptions OptimizedAndComposite => new BundlingOptions(true, true);
|
||||
public static BundlingOptions OptimizedNotComposite => new BundlingOptions(true, false);
|
||||
public static BundlingOptions NotOptimizedNotComposite => new BundlingOptions(false, false);
|
||||
public static BundlingOptions NotOptimizedAndComposite => new BundlingOptions(false, true);
|
||||
|
||||
public BundlingOptions(bool optimizeOutput = true, bool enabledCompositeFiles = true)
|
||||
{
|
||||
OptimizeOutput = optimizeOutput;
|
||||
EnabledCompositeFiles = enabledCompositeFiles;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If true, the files in the bundle will be minified
|
||||
/// </summary>
|
||||
public bool OptimizeOutput { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If true, the files in the bundle will be combined, if false the files
|
||||
/// will be served as individual files.
|
||||
/// </summary>
|
||||
public bool EnabledCompositeFiles { get; }
|
||||
|
||||
public override bool Equals(object obj) => obj is BundlingOptions options && Equals(options);
|
||||
public bool Equals(BundlingOptions other) => OptimizeOutput == other.OptimizeOutput && EnabledCompositeFiles == other.EnabledCompositeFiles;
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
int hashCode = 2130304063;
|
||||
hashCode = hashCode * -1521134295 + OptimizeOutput.GetHashCode();
|
||||
hashCode = hashCode * -1521134295 + EnabledCompositeFiles.GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
public static bool operator ==(BundlingOptions left, BundlingOptions right) => left.Equals(right);
|
||||
|
||||
public static bool operator !=(BundlingOptions left, BundlingOptions right) => !(left == right);
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ namespace Umbraco.Cms.Core.WebAssets
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown if any of the paths specified are not absolute
|
||||
/// </exception>
|
||||
void CreateCssBundle(string bundleName, bool optimizeOutput, params string[] filePaths);
|
||||
void CreateCssBundle(string bundleName, BundlingOptions bundleOptions, params string[] filePaths);
|
||||
|
||||
/// <summary>
|
||||
/// Renders the html link tag for the bundle
|
||||
@@ -48,7 +48,7 @@ namespace Umbraco.Cms.Core.WebAssets
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown if any of the paths specified are not absolute
|
||||
/// </exception>
|
||||
void CreateJsBundle(string bundleName, bool optimizeOutput, params string[] filePaths);
|
||||
void CreateJsBundle(string bundleName, BundlingOptions bundleOptions, params string[] filePaths);
|
||||
|
||||
/// <summary>
|
||||
/// Renders the html script tag for the bundle
|
||||
|
||||
@@ -30,11 +30,6 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SecurityCodeScan">
|
||||
<Version>3.5.4</Version>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Umbraco.Code" Version="1.1.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -2,12 +2,8 @@ using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Cms.Core.Services.Changes;
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
using Umbraco.Cms.Infrastructure.Search;
|
||||
using Umbraco.Cms.Infrastructure.Sync;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
@@ -40,49 +36,67 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the server registrar.</typeparam>
|
||||
/// <param name="builder">The builder.</param>
|
||||
public static void SetServerRegistrar<T>(this IUmbracoBuilder builder)
|
||||
public static IUmbracoBuilder SetServerRegistrar<T>(this IUmbracoBuilder builder)
|
||||
where T : class, IServerRoleAccessor
|
||||
=> builder.Services.AddUnique<IServerRoleAccessor, T>();
|
||||
{
|
||||
builder.Services.AddUnique<IServerRoleAccessor, T>();
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the server registrar.
|
||||
/// </summary>
|
||||
/// <param name="builder">The builder.</param>
|
||||
/// <param name="factory">A function creating a server registrar.</param>
|
||||
public static void SetServerRegistrar(this IUmbracoBuilder builder, Func<IServiceProvider, IServerRoleAccessor> factory)
|
||||
=> builder.Services.AddUnique(factory);
|
||||
public static IUmbracoBuilder SetServerRegistrar(this IUmbracoBuilder builder, Func<IServiceProvider, IServerRoleAccessor> factory)
|
||||
{
|
||||
builder.Services.AddUnique(factory);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the server registrar.
|
||||
/// </summary>
|
||||
/// <param name="builder">The builder.</param>
|
||||
/// <param name="registrar">A server registrar.</param>
|
||||
public static void SetServerRegistrar(this IUmbracoBuilder builder, IServerRoleAccessor registrar)
|
||||
=> builder.Services.AddUnique(registrar);
|
||||
public static IUmbracoBuilder SetServerRegistrar(this IUmbracoBuilder builder, IServerRoleAccessor registrar)
|
||||
{
|
||||
builder.Services.AddUnique(registrar);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the server messenger.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the server registrar.</typeparam>
|
||||
/// <param name="builder">The builder.</param>
|
||||
public static void SetServerMessenger<T>(this IUmbracoBuilder builder)
|
||||
public static IUmbracoBuilder SetServerMessenger<T>(this IUmbracoBuilder builder)
|
||||
where T : class, IServerMessenger
|
||||
=> builder.Services.AddUnique<IServerMessenger, T>();
|
||||
{
|
||||
builder.Services.AddUnique<IServerMessenger, T>();
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the server messenger.
|
||||
/// </summary>
|
||||
/// <param name="builder">The builder.</param>
|
||||
/// <param name="factory">A function creating a server messenger.</param>
|
||||
public static void SetServerMessenger(this IUmbracoBuilder builder, Func<IServiceProvider, IServerMessenger> factory)
|
||||
=> builder.Services.AddUnique(factory);
|
||||
public static IUmbracoBuilder SetServerMessenger(this IUmbracoBuilder builder, Func<IServiceProvider, IServerMessenger> factory)
|
||||
{
|
||||
builder.Services.AddUnique(factory);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the server messenger.
|
||||
/// </summary>
|
||||
/// <param name="builder">The builder.</param>
|
||||
/// <param name="registrar">A server messenger.</param>
|
||||
public static void SetServerMessenger(this IUmbracoBuilder builder, IServerMessenger registrar)
|
||||
=> builder.Services.AddUnique(registrar);
|
||||
public static IUmbracoBuilder SetServerMessenger(this IUmbracoBuilder builder, IServerMessenger registrar)
|
||||
{
|
||||
builder.Services.AddUnique(registrar);
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
builder.Services.AddUnique<IDomainService, DomainService>();
|
||||
builder.Services.AddUnique<IAuditService, AuditService>();
|
||||
builder.Services.AddUnique<ICacheInstructionService, CacheInstructionService>();
|
||||
builder.Services.AddUnique<IBasicAuthService, BasicAuthService>();
|
||||
builder.Services.AddUnique<ITagService, TagService>();
|
||||
builder.Services.AddUnique<IContentService, ContentService>();
|
||||
builder.Services.AddUnique<IUserService, UserService>();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Dictionary;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
@@ -21,10 +20,11 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the factory.</typeparam>
|
||||
/// <param name="builder">The builder.</param>
|
||||
public static void SetCultureDictionaryFactory<T>(this IUmbracoBuilder builder)
|
||||
public static IUmbracoBuilder SetCultureDictionaryFactory<T>(this IUmbracoBuilder builder)
|
||||
where T : class, ICultureDictionaryFactory
|
||||
{
|
||||
builder.Services.AddUnique<ICultureDictionaryFactory, T>();
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -32,9 +32,10 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
/// </summary>
|
||||
/// <param name="builder">The builder.</param>
|
||||
/// <param name="factory">A function creating a culture dictionary factory.</param>
|
||||
public static void SetCultureDictionaryFactory(this IUmbracoBuilder builder, Func<IServiceProvider, ICultureDictionaryFactory> factory)
|
||||
public static IUmbracoBuilder SetCultureDictionaryFactory(this IUmbracoBuilder builder, Func<IServiceProvider, ICultureDictionaryFactory> factory)
|
||||
{
|
||||
builder.Services.AddUnique(factory);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -42,9 +43,10 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
/// </summary>
|
||||
/// <param name="builder">The builder.</param>
|
||||
/// <param name="factory">A factory.</param>
|
||||
public static void SetCultureDictionaryFactory(this IUmbracoBuilder builder, ICultureDictionaryFactory factory)
|
||||
public static IUmbracoBuilder SetCultureDictionaryFactory(this IUmbracoBuilder builder, ICultureDictionaryFactory factory)
|
||||
{
|
||||
builder.Services.AddUnique(factory);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -52,10 +54,11 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the factory.</typeparam>
|
||||
/// <param name="builder">The builder.</param>
|
||||
public static void SetPublishedContentModelFactory<T>(this IUmbracoBuilder builder)
|
||||
public static IUmbracoBuilder SetPublishedContentModelFactory<T>(this IUmbracoBuilder builder)
|
||||
where T : class, IPublishedModelFactory
|
||||
{
|
||||
builder.Services.AddUnique<IPublishedModelFactory, T>();
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -63,9 +66,10 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
/// </summary>
|
||||
/// <param name="builder">The builder.</param>
|
||||
/// <param name="factory">A function creating a published content model factory.</param>
|
||||
public static void SetPublishedContentModelFactory(this IUmbracoBuilder builder, Func<IServiceProvider, IPublishedModelFactory> factory)
|
||||
public static IUmbracoBuilder SetPublishedContentModelFactory(this IUmbracoBuilder builder, Func<IServiceProvider, IPublishedModelFactory> factory)
|
||||
{
|
||||
builder.Services.AddUnique(factory);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -73,9 +77,10 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
/// </summary>
|
||||
/// <param name="builder">The builder.</param>
|
||||
/// <param name="factory">A published content model factory.</param>
|
||||
public static void SetPublishedContentModelFactory(this IUmbracoBuilder builder, IPublishedModelFactory factory)
|
||||
public static IUmbracoBuilder SetPublishedContentModelFactory(this IUmbracoBuilder builder, IPublishedModelFactory factory)
|
||||
{
|
||||
builder.Services.AddUnique(factory);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -83,10 +88,11 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the short string helper.</typeparam>
|
||||
/// <param name="builder">The builder.</param>
|
||||
public static void SetShortStringHelper<T>(this IUmbracoBuilder builder)
|
||||
public static IUmbracoBuilder SetShortStringHelper<T>(this IUmbracoBuilder builder)
|
||||
where T : class, IShortStringHelper
|
||||
{
|
||||
builder.Services.AddUnique<IShortStringHelper, T>();
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -94,9 +100,10 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
/// </summary>
|
||||
/// <param name="builder">The builder.</param>
|
||||
/// <param name="factory">A function creating a short string helper.</param>
|
||||
public static void SetShortStringHelper(this IUmbracoBuilder builder, Func<IServiceProvider, IShortStringHelper> factory)
|
||||
public static IUmbracoBuilder SetShortStringHelper(this IUmbracoBuilder builder, Func<IServiceProvider, IShortStringHelper> factory)
|
||||
{
|
||||
builder.Services.AddUnique(factory);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -104,9 +111,10 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
/// </summary>
|
||||
/// <param name="builder">A builder.</param>
|
||||
/// <param name="helper">A short string helper.</param>
|
||||
public static void SetShortStringHelper(this IUmbracoBuilder builder, IShortStringHelper helper)
|
||||
public static IUmbracoBuilder SetShortStringHelper(this IUmbracoBuilder builder, IShortStringHelper helper)
|
||||
{
|
||||
builder.Services.AddUnique(helper);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -114,19 +122,23 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
/// </summary>
|
||||
/// <param name="builder">A builder.</param>
|
||||
/// <param name="filesystemFactory">Factory method to create an IFileSystem implementation used in the MediaFileManager</param>
|
||||
public static void SetMediaFileSystem(this IUmbracoBuilder builder,
|
||||
Func<IServiceProvider, IFileSystem> filesystemFactory) => builder.Services.AddUnique(
|
||||
provider =>
|
||||
{
|
||||
IFileSystem filesystem = filesystemFactory(provider);
|
||||
// We need to use the Filesystems to create a shadow wrapper,
|
||||
// because shadow wrapper requires the IsScoped delegate from the FileSystems.
|
||||
// This is used by the scope provider when taking control of the filesystems.
|
||||
FileSystems fileSystems = provider.GetRequiredService<FileSystems>();
|
||||
IFileSystem shadow = fileSystems.CreateShadowWrapper(filesystem, "media");
|
||||
public static IUmbracoBuilder SetMediaFileSystem(this IUmbracoBuilder builder,
|
||||
Func<IServiceProvider, IFileSystem> filesystemFactory)
|
||||
{
|
||||
builder.Services.AddUnique(
|
||||
provider =>
|
||||
{
|
||||
IFileSystem filesystem = filesystemFactory(provider);
|
||||
// We need to use the Filesystems to create a shadow wrapper,
|
||||
// because shadow wrapper requires the IsScoped delegate from the FileSystems.
|
||||
// This is used by the scope provider when taking control of the filesystems.
|
||||
FileSystems fileSystems = provider.GetRequiredService<FileSystems>();
|
||||
IFileSystem shadow = fileSystems.CreateShadowWrapper(filesystem, "media");
|
||||
|
||||
return provider.CreateInstance<MediaFileManager>(shadow);
|
||||
});
|
||||
return provider.CreateInstance<MediaFileManager>(shadow);
|
||||
});
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register FileSystems with a method to configure the <see cref="FileSystems"/>.
|
||||
@@ -135,7 +147,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
/// <param name="configure">Method that configures the <see cref="FileSystems"/>.</param>
|
||||
/// <exception cref="ArgumentNullException">Throws exception if <paramref name="configure"/> is null.</exception>
|
||||
/// <exception cref="InvalidOperationException">Throws exception if full path can't be resolved successfully.</exception>
|
||||
public static void ConfigureFileSystems(this IUmbracoBuilder builder,
|
||||
public static IUmbracoBuilder ConfigureFileSystems(this IUmbracoBuilder builder,
|
||||
Action<IServiceProvider, FileSystems> configure)
|
||||
{
|
||||
if (configure == null)
|
||||
@@ -150,6 +162,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
configure(provider, fileSystems);
|
||||
return fileSystems;
|
||||
});
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -157,10 +170,11 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the log viewer.</typeparam>
|
||||
/// <param name="builder">The builder.</param>
|
||||
public static void SetLogViewer<T>(this IUmbracoBuilder builder)
|
||||
public static IUmbracoBuilder SetLogViewer<T>(this IUmbracoBuilder builder)
|
||||
where T : class, ILogViewer
|
||||
{
|
||||
builder.Services.AddUnique<ILogViewer, T>();
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -168,19 +182,21 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
/// </summary>
|
||||
/// <param name="builder">The builder.</param>
|
||||
/// <param name="factory">A function creating a log viewer.</param>
|
||||
public static void SetLogViewer(this IUmbracoBuilder builder, Func<IServiceProvider, ILogViewer> factory)
|
||||
public static IUmbracoBuilder SetLogViewer(this IUmbracoBuilder builder, Func<IServiceProvider, ILogViewer> factory)
|
||||
{
|
||||
builder.Services.AddUnique(factory);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the log viewer.
|
||||
/// </summary>
|
||||
/// <param name="builder">A builder.</param>
|
||||
/// <param name="helper">A log viewer.</param>
|
||||
public static void SetLogViewer(this IUmbracoBuilder builder, ILogViewer viewer)
|
||||
/// <param name="viewer">A log viewer.</param>
|
||||
public static IUmbracoBuilder SetLogViewer(this IUmbracoBuilder builder, ILogViewer viewer)
|
||||
{
|
||||
builder.Services.AddUnique(viewer);
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using System.Net.Mail;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
@@ -23,12 +24,14 @@ namespace Umbraco.Cms.Infrastructure
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly bool _notificationHandlerRegistered;
|
||||
private readonly ILogger<EmailSender> _logger;
|
||||
|
||||
public EmailSender(IOptions<GlobalSettings> globalSettings, IEventAggregator eventAggregator)
|
||||
public EmailSender(
|
||||
ILogger<EmailSender> logger,
|
||||
IOptions<GlobalSettings> globalSettings,
|
||||
IEventAggregator eventAggregator)
|
||||
: this(globalSettings, eventAggregator, null)
|
||||
{
|
||||
|
||||
}
|
||||
=> _logger = logger;
|
||||
|
||||
public EmailSender(IOptions<GlobalSettings> globalSettings, IEventAggregator eventAggregator, INotificationHandler<SendEmailNotification> handler)
|
||||
{
|
||||
@@ -49,39 +52,47 @@ namespace Umbraco.Cms.Infrastructure
|
||||
|
||||
private async Task SendAsyncInternal(EmailMessage message, bool enableNotification)
|
||||
{
|
||||
if (enableNotification)
|
||||
{
|
||||
var notification = new SendEmailNotification(message.ToNotificationEmail(_globalSettings.Smtp?.From));
|
||||
await _eventAggregator.PublishAsync(notification);
|
||||
|
||||
// if a handler handled sending the email then don't continue.
|
||||
if (notification.IsHandled)
|
||||
{
|
||||
_logger.LogDebug("The email sending for {Subject} was handled by a notification handler", notification.Message.Subject);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (_globalSettings.IsSmtpServerConfigured == false)
|
||||
{
|
||||
if (enableNotification)
|
||||
{
|
||||
await _eventAggregator.PublishAsync(
|
||||
new SendEmailNotification(message.ToNotificationEmail(_globalSettings.Smtp?.From)));
|
||||
}
|
||||
_logger.LogDebug("Could not send email for {Subject}. It was not handled by a notification handler and there is no SMTP configured.", message.Subject);
|
||||
return;
|
||||
}
|
||||
|
||||
using (var client = new SmtpClient())
|
||||
using var client = new SmtpClient();
|
||||
|
||||
await client.ConnectAsync(_globalSettings.Smtp.Host,
|
||||
_globalSettings.Smtp.Port,
|
||||
(MailKit.Security.SecureSocketOptions)(int)_globalSettings.Smtp.SecureSocketOptions);
|
||||
|
||||
if (!(_globalSettings.Smtp.Username is null && _globalSettings.Smtp.Password is null))
|
||||
{
|
||||
await client.ConnectAsync(_globalSettings.Smtp.Host,
|
||||
_globalSettings.Smtp.Port,
|
||||
(MailKit.Security.SecureSocketOptions)(int)_globalSettings.Smtp.SecureSocketOptions);
|
||||
|
||||
if (!(_globalSettings.Smtp.Username is null && _globalSettings.Smtp.Password is null))
|
||||
{
|
||||
await client.AuthenticateAsync(_globalSettings.Smtp.Username, _globalSettings.Smtp.Password);
|
||||
}
|
||||
|
||||
var mailMessage = message.ToMimeMessage(_globalSettings.Smtp.From);
|
||||
if (_globalSettings.Smtp.DeliveryMethod == SmtpDeliveryMethod.Network)
|
||||
{
|
||||
await client.SendAsync(mailMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
client.Send(mailMessage);
|
||||
}
|
||||
|
||||
await client.DisconnectAsync(true);
|
||||
await client.AuthenticateAsync(_globalSettings.Smtp.Username, _globalSettings.Smtp.Password);
|
||||
}
|
||||
|
||||
var mailMessage = message.ToMimeMessage(_globalSettings.Smtp.From);
|
||||
if (_globalSettings.Smtp.DeliveryMethod == SmtpDeliveryMethod.Network)
|
||||
{
|
||||
await client.SendAsync(mailMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
client.Send(mailMessage);
|
||||
}
|
||||
|
||||
await client.DisconnectAsync(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
@@ -15,17 +14,6 @@ namespace Umbraco.Cms.Core.Logging.Serilog
|
||||
{
|
||||
public global::Serilog.ILogger SerilogLog { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a new instance of the <see cref="SerilogLogger"/> class with a configuration file.
|
||||
/// </summary>
|
||||
/// <param name="logConfigFile"></param>
|
||||
public SerilogLogger(FileInfo logConfigFile)
|
||||
{
|
||||
SerilogLog = new LoggerConfiguration()
|
||||
.ReadFrom.AppSettings(filePath: logConfigFile.FullName)
|
||||
.CreateLogger();
|
||||
}
|
||||
|
||||
public SerilogLogger(LoggerConfiguration logConfig)
|
||||
{
|
||||
//Configure Serilog static global logger with config passed in
|
||||
|
||||
@@ -62,22 +62,22 @@ namespace Umbraco.Cms.Core.Manifest
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ManifestParser"/> class.
|
||||
/// </summary>
|
||||
private ManifestParser(AppCaches appCaches, ManifestValueValidatorCollection validators, ManifestFilterCollection filters, string path, ILogger<ManifestParser> logger, IIOHelper ioHelper, IHostingEnvironment hostingEnvironment)
|
||||
private ManifestParser(AppCaches appCaches, ManifestValueValidatorCollection validators, ManifestFilterCollection filters, string appPluginsPath, ILogger<ManifestParser> logger, IIOHelper ioHelper, IHostingEnvironment hostingEnvironment)
|
||||
{
|
||||
if (appCaches == null) throw new ArgumentNullException(nameof(appCaches));
|
||||
_cache = appCaches.RuntimeCache;
|
||||
_validators = validators ?? throw new ArgumentNullException(nameof(validators));
|
||||
_filters = filters ?? throw new ArgumentNullException(nameof(filters));
|
||||
if (path == null) throw new ArgumentNullException(nameof(path));
|
||||
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(path));
|
||||
if (appPluginsPath == null) throw new ArgumentNullException(nameof(appPluginsPath));
|
||||
if (string.IsNullOrWhiteSpace(appPluginsPath)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(appPluginsPath));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_ioHelper = ioHelper;
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
|
||||
Path = path;
|
||||
AppPluginsPath = appPluginsPath;
|
||||
}
|
||||
|
||||
public string Path
|
||||
public string AppPluginsPath
|
||||
{
|
||||
get => _path;
|
||||
set => _path = value.StartsWith("~/") ? _hostingEnvironment.MapPathContentRoot(value) : value;
|
||||
@@ -87,11 +87,12 @@ namespace Umbraco.Cms.Core.Manifest
|
||||
/// Gets all manifests, merged into a single manifest object.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public PackageManifest Manifest
|
||||
=> _cache.GetCacheItem<PackageManifest>("Umbraco.Core.Manifest.ManifestParser::Manifests", () =>
|
||||
public CompositePackageManifest CombinedManifest
|
||||
=> _cache.GetCacheItem<CompositePackageManifest>("Umbraco.Core.Manifest.ManifestParser::Manifests", () =>
|
||||
{
|
||||
var manifests = GetManifests();
|
||||
IEnumerable<PackageManifest> manifests = GetManifests();
|
||||
return MergeManifests(manifests);
|
||||
|
||||
}, new TimeSpan(0, 4, 0));
|
||||
|
||||
/// <summary>
|
||||
@@ -130,10 +131,10 @@ namespace Umbraco.Cms.Core.Manifest
|
||||
/// <summary>
|
||||
/// Merges all manifests into one.
|
||||
/// </summary>
|
||||
private static PackageManifest MergeManifests(IEnumerable<PackageManifest> manifests)
|
||||
private static CompositePackageManifest MergeManifests(IEnumerable<PackageManifest> manifests)
|
||||
{
|
||||
var scripts = new HashSet<string>();
|
||||
var stylesheets = new HashSet<string>();
|
||||
var scripts = new Dictionary<BundleOptions, List<ManifestAssets>>();
|
||||
var stylesheets = new Dictionary<BundleOptions, List<ManifestAssets>>();
|
||||
var propertyEditors = new List<IDataEditor>();
|
||||
var parameterEditors = new List<IDataEditor>();
|
||||
var gridEditors = new List<GridEditor>();
|
||||
@@ -141,10 +142,28 @@ namespace Umbraco.Cms.Core.Manifest
|
||||
var dashboards = new List<ManifestDashboard>();
|
||||
var sections = new List<ManifestSection>();
|
||||
|
||||
foreach (var manifest in manifests)
|
||||
foreach (PackageManifest manifest in manifests)
|
||||
{
|
||||
if (manifest.Scripts != null) foreach (var script in manifest.Scripts) scripts.Add(script);
|
||||
if (manifest.Stylesheets != null) foreach (var stylesheet in manifest.Stylesheets) stylesheets.Add(stylesheet);
|
||||
if (manifest.Scripts != null)
|
||||
{
|
||||
if (!scripts.TryGetValue(manifest.BundleOptions, out List<ManifestAssets> scriptsPerBundleOption))
|
||||
{
|
||||
scriptsPerBundleOption = new List<ManifestAssets>();
|
||||
scripts[manifest.BundleOptions] = scriptsPerBundleOption;
|
||||
}
|
||||
scriptsPerBundleOption.Add(new ManifestAssets(manifest.PackageName, manifest.Scripts));
|
||||
}
|
||||
|
||||
if (manifest.Stylesheets != null)
|
||||
{
|
||||
if (!stylesheets.TryGetValue(manifest.BundleOptions, out List<ManifestAssets> stylesPerBundleOption))
|
||||
{
|
||||
stylesPerBundleOption = new List<ManifestAssets>();
|
||||
stylesheets[manifest.BundleOptions] = stylesPerBundleOption;
|
||||
}
|
||||
stylesPerBundleOption.Add(new ManifestAssets(manifest.PackageName, manifest.Stylesheets));
|
||||
}
|
||||
|
||||
if (manifest.PropertyEditors != null) propertyEditors.AddRange(manifest.PropertyEditors);
|
||||
if (manifest.ParameterEditors != null) parameterEditors.AddRange(manifest.ParameterEditors);
|
||||
if (manifest.GridEditors != null) gridEditors.AddRange(manifest.GridEditors);
|
||||
@@ -153,17 +172,15 @@ namespace Umbraco.Cms.Core.Manifest
|
||||
if (manifest.Sections != null) sections.AddRange(manifest.Sections.DistinctBy(x => x.Alias.ToLowerInvariant()));
|
||||
}
|
||||
|
||||
return new PackageManifest
|
||||
{
|
||||
Scripts = scripts.ToArray(),
|
||||
Stylesheets = stylesheets.ToArray(),
|
||||
PropertyEditors = propertyEditors.ToArray(),
|
||||
ParameterEditors = parameterEditors.ToArray(),
|
||||
GridEditors = gridEditors.ToArray(),
|
||||
ContentApps = contentApps.ToArray(),
|
||||
Dashboards = dashboards.ToArray(),
|
||||
Sections = sections.ToArray()
|
||||
};
|
||||
return new CompositePackageManifest(
|
||||
propertyEditors,
|
||||
parameterEditors,
|
||||
gridEditors,
|
||||
contentApps,
|
||||
dashboards,
|
||||
sections,
|
||||
scripts.ToDictionary(x => x.Key, x => (IReadOnlyList<ManifestAssets>)x.Value),
|
||||
stylesheets.ToDictionary(x => x.Key, x => (IReadOnlyList<ManifestAssets>)x.Value));
|
||||
}
|
||||
|
||||
// gets all manifest files (recursively)
|
||||
|
||||
@@ -9,6 +9,8 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Expressions.Create.Table
|
||||
public class CreateTableOfDtoBuilder : IExecutableBuilder
|
||||
{
|
||||
private readonly IMigrationContext _context;
|
||||
|
||||
// TODO: This doesn't do anything.
|
||||
private readonly DatabaseType[] _supportedDatabaseTypes;
|
||||
|
||||
public CreateTableOfDtoBuilder(IMigrationContext context, params DatabaseType[] supportedDatabaseTypes)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NPoco;
|
||||
@@ -151,7 +151,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
|
||||
//New UDI pickers with newer Ids
|
||||
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1046, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1046", SortOrder = 2, UniqueId = new Guid("FD1E0DA5-5606-4862-B679-5D0CF3A52A59"), Text = "Content Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now });
|
||||
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1047, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1047", SortOrder = 2, UniqueId = new Guid("1EA2E01F-EBD8-4CE1-8D71-6B1149E63548"), Text = "Member Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now });
|
||||
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1048, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1048", SortOrder = 2, UniqueId = new Guid("135D60E0-64D9-49ED-AB08-893C9BA44AE5"), Text = "Media Picke (legacy)", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now });
|
||||
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1048, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1048", SortOrder = 2, UniqueId = new Guid("135D60E0-64D9-49ED-AB08-893C9BA44AE5"), Text = "Media Picker (legacy)", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now });
|
||||
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1049, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1049", SortOrder = 2, UniqueId = new Guid("9DBBCBBB-2327-434A-B355-AF1B84E5010A"), Text = "Multiple Media Picker (legacy)", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now });
|
||||
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1050, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1050", SortOrder = 2, UniqueId = new Guid("B4E3535A-1753-47E2-8568-602CF8CFEE6F"), Text = "Multi URL Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now });
|
||||
|
||||
|
||||
@@ -211,8 +211,8 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade
|
||||
Merge()
|
||||
.To<AddCmsContentNuByteColumn>("{8DDDCD0B-D7D5-4C97-BD6A-6B38CA65752F}")
|
||||
.To<UpgradedIncludeIndexes>("{4695D0C9-0729-4976-985B-048D503665D8}")
|
||||
|
||||
// to 9.0.0
|
||||
.To<UpdateCmsPropertyGroupIdSeed>("{5C424554-A32D-4852-8ED1-A13508187901}")
|
||||
// to 9.0.0 RC
|
||||
.With()
|
||||
.To<MigrateLogViewerQueriesFromFileToDb>("{22D801BA-A1FF-4539-BFCC-2139B55594F8}")
|
||||
.To<ExternalLoginTableIndexes>("{50A43237-A6F4-49E2-A7A6-5DAD65C84669}")
|
||||
@@ -222,6 +222,17 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade
|
||||
|
||||
//FINAL
|
||||
.As("{5060F3D2-88BE-4D30-8755-CF51F28EAD12}");
|
||||
|
||||
// TO 9.0.0
|
||||
|
||||
|
||||
// This should be safe to execute again. We need it with a new name to ensure updates from all the following has executed this step.
|
||||
// - 8.15 RC - Current state: {4695D0C9-0729-4976-985B-048D503665D8}
|
||||
// - 8.15 Final - Current state: {5C424554-A32D-4852-8ED1-A13508187901}
|
||||
// - 9.0 RC1 - Current state: {5060F3D2-88BE-4D30-8755-CF51F28EAD12}
|
||||
To<UpdateCmsPropertyGroupIdSeed>("{622E5172-42E1-4662-AD80-9504AF5A4E53}");
|
||||
|
||||
To<ExternalLoginTableIndexesFixup>("{10F7BB61-C550-426B-830B-7F954F689CDF}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using NPoco;
|
||||
using System.Linq;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
|
||||
using Umbraco.Cms.Infrastructure.Persistence;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_15_0
|
||||
{
|
||||
@@ -14,12 +17,46 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_15_0
|
||||
|
||||
protected override void Migrate()
|
||||
{
|
||||
// allow null for the `data` field
|
||||
if (DatabaseType.IsSqlCe())
|
||||
{
|
||||
// SQLCE does not support altering NTEXT, so we have to jump through some hoops to do it
|
||||
// All column ordering must remain the same as what is defined in the DTO so we need to create a temp table,
|
||||
// drop orig and then re-create/copy.
|
||||
Create.Table<ContentNuDtoTemp>(withoutKeysAndIndexes: true).Do();
|
||||
Execute.Sql($"INSERT INTO [{TempTableName}] SELECT nodeId, published, data, rv FROM [{Constants.DatabaseSchema.Tables.NodeData}]").Do();
|
||||
Delete.Table(Constants.DatabaseSchema.Tables.NodeData).Do();
|
||||
Create.Table<ContentNuDto>().Do();
|
||||
Execute.Sql($"INSERT INTO [{Constants.DatabaseSchema.Tables.NodeData}] SELECT nodeId, published, data, rv, NULL FROM [{TempTableName}]").Do();
|
||||
}
|
||||
else
|
||||
{
|
||||
AlterColumn<ContentNuDto>(Constants.DatabaseSchema.Tables.NodeData, "data");
|
||||
}
|
||||
|
||||
var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToList();
|
||||
|
||||
AddColumnIfNotExists<ContentNuDto>(columns, "dataRaw");
|
||||
}
|
||||
|
||||
// allow null
|
||||
AlterColumn<ContentNuDto>(Constants.DatabaseSchema.Tables.NodeData, "data");
|
||||
private const string TempTableName = Constants.DatabaseSchema.TableNamePrefix + "cms" + "ContentNuTEMP";
|
||||
|
||||
[TableName(TempTableName)]
|
||||
[ExplicitColumns]
|
||||
private class ContentNuDtoTemp
|
||||
{
|
||||
[Column("nodeId")]
|
||||
public int NodeId { get; set; }
|
||||
|
||||
[Column("published")]
|
||||
public bool Published { get; set; }
|
||||
|
||||
[Column("data")]
|
||||
[SpecialDbType(SpecialDbTypes.NTEXT)]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
public string Data { get; set; }
|
||||
|
||||
[Column("rv")]
|
||||
public long Rv { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
using Umbraco.Cms.Infrastructure.Persistence;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_15_0
|
||||
{
|
||||
public class UpdateCmsPropertyGroupIdSeed : MigrationBase
|
||||
{
|
||||
public UpdateCmsPropertyGroupIdSeed(IMigrationContext context) : base(context)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Migrate()
|
||||
{
|
||||
if (DatabaseType.IsSqlCe())
|
||||
{
|
||||
Database.Execute(Sql("ALTER TABLE [cmsPropertyTypeGroup] ALTER COLUMN [id] IDENTITY (56,1)"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0
|
||||
{
|
||||
|
||||
public class ExternalLoginTableIndexes : MigrationBase
|
||||
{
|
||||
public ExternalLoginTableIndexes(IMigrationContext context)
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0
|
||||
{
|
||||
/// <summary>
|
||||
/// Fixes up the original <see cref="ExternalLoginTableIndexes"/> for post RC release to ensure that
|
||||
/// the correct indexes are applied.
|
||||
/// </summary>
|
||||
public class ExternalLoginTableIndexesFixup : MigrationBase
|
||||
{
|
||||
public ExternalLoginTableIndexesFixup(IMigrationContext context) : base(context)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Migrate()
|
||||
{
|
||||
var indexName1 = "IX_" + ExternalLoginDto.TableName + "_LoginProvider";
|
||||
var indexName2 = "IX_" + ExternalLoginDto.TableName + "_ProviderKey";
|
||||
|
||||
if (IndexExists(indexName1))
|
||||
{
|
||||
// drop it since the previous migration index was wrong, and we
|
||||
// need to modify a column that belons to it
|
||||
Delete.Index(indexName1).OnTable(ExternalLoginDto.TableName).Do();
|
||||
}
|
||||
|
||||
if (IndexExists(indexName2))
|
||||
{
|
||||
// drop since it's using a column we're about to modify
|
||||
Delete.Index(indexName2).OnTable(ExternalLoginDto.TableName).Do();
|
||||
}
|
||||
|
||||
// then fixup the length of the loginProvider column
|
||||
AlterColumn<ExternalLoginDto>(ExternalLoginDto.TableName, "loginProvider");
|
||||
|
||||
// create it with the correct definition
|
||||
Create
|
||||
.Index(indexName1)
|
||||
.OnTable(ExternalLoginDto.TableName)
|
||||
.OnColumn("loginProvider").Ascending()
|
||||
.OnColumn("userId").Ascending()
|
||||
.WithOptions()
|
||||
.Unique()
|
||||
.WithOptions()
|
||||
.NonClustered()
|
||||
.Do();
|
||||
|
||||
// re-create the original
|
||||
Create
|
||||
.Index(indexName2)
|
||||
.OnTable(ExternalLoginDto.TableName)
|
||||
.OnColumn("loginProvider").Ascending()
|
||||
.OnColumn("providerKey").Ascending()
|
||||
.WithOptions()
|
||||
.NonClustered()
|
||||
.Do();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.PropertyEditors.ValueConverters;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
namespace Umbraco.Cms.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a media item with local crops.
|
||||
|
||||
@@ -98,6 +98,13 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder.Building
|
||||
sb.AppendFormat("{0}[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Umbraco.ModelsBuilder.Embedded\", \"{1}\")]\n", tabs, ApiVersion.Current.Version);
|
||||
}
|
||||
|
||||
// writes an attribute that specifies that an output may be null.
|
||||
// (useful for consuming projects with nullable reference types enabled)
|
||||
private static void WriteMaybeNullAttribute(StringBuilder sb, string tabs, bool isReturn = false)
|
||||
{
|
||||
sb.AppendFormat("{0}[{1}global::System.Diagnostics.CodeAnalysis.MaybeNull]\n", tabs, isReturn ? "return: " : "");
|
||||
}
|
||||
|
||||
private void WriteContentType(StringBuilder sb, TypeModel type)
|
||||
{
|
||||
string sep;
|
||||
@@ -185,9 +192,11 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder.Building
|
||||
sb.AppendFormat("\t\tpublic new const PublishedItemType ModelItemType = PublishedItemType.{0};\n",
|
||||
itemType);
|
||||
WriteGeneratedCodeAttribute(sb, "\t\t");
|
||||
WriteMaybeNullAttribute(sb, "\t\t", true);
|
||||
sb.Append("\t\tpublic new static IPublishedContentType GetModelContentType(IPublishedSnapshotAccessor publishedSnapshotAccessor)\n");
|
||||
sb.Append("\t\t\t=> PublishedModelUtility.GetModelContentType(publishedSnapshotAccessor, ModelItemType, ModelTypeAlias);\n");
|
||||
WriteGeneratedCodeAttribute(sb, "\t\t");
|
||||
WriteMaybeNullAttribute(sb, "\t\t", true);
|
||||
sb.AppendFormat("\t\tpublic static IPublishedPropertyType GetModelPropertyType<TValue>(IPublishedSnapshotAccessor publishedSnapshotAccessor, Expression<Func<{0}, TValue>> selector)\n",
|
||||
type.ClrName);
|
||||
sb.Append("\t\t\t=> PublishedModelUtility.GetModelPropertyType(GetModelContentType(publishedSnapshotAccessor), selector);\n");
|
||||
@@ -305,6 +314,8 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder.Building
|
||||
}
|
||||
|
||||
WriteGeneratedCodeAttribute(sb, "\t\t");
|
||||
if (!property.ModelClrType.IsValueType)
|
||||
WriteMaybeNullAttribute(sb, "\t\t");
|
||||
sb.AppendFormat("\t\t[ImplementPropertyType(\"{0}\")]\n", property.Alias);
|
||||
|
||||
if (mixinStatic)
|
||||
@@ -349,6 +360,8 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder.Building
|
||||
sb.AppendFormat("\t\t/// <summary>Static getter for {0}</summary>\n", XmlCommentString(property.Name));
|
||||
|
||||
WriteGeneratedCodeAttribute(sb, "\t\t");
|
||||
if (!property.ModelClrType.IsValueType)
|
||||
WriteMaybeNullAttribute(sb, "\t\t", true);
|
||||
sb.Append("\t\tpublic static ");
|
||||
WriteClrType(sb, property.ClrTypeName);
|
||||
sb.AppendFormat(" {0}(I{1} that, IPublishedValueFallback publishedValueFallback) => that.Value",
|
||||
@@ -404,6 +417,9 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder.Building
|
||||
if (!string.IsNullOrWhiteSpace(property.Name))
|
||||
sb.AppendFormat("\t\t/// <summary>{0}</summary>\n", XmlCommentString(property.Name));
|
||||
WriteGeneratedCodeAttribute(sb, "\t\t");
|
||||
if (!property.ModelClrType.IsValueType)
|
||||
WriteMaybeNullAttribute(sb, "\t\t");
|
||||
|
||||
sb.Append("\t\t");
|
||||
WriteClrType(sb, property.ClrTypeName);
|
||||
sb.AppendFormat(" {0} {{ get; }}\n",
|
||||
|
||||
@@ -13,10 +13,9 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
|
||||
public int BulkInsertRecords<T>(IUmbracoDatabase database, IEnumerable<T> records)
|
||||
{
|
||||
var recordsA = records.ToArray();
|
||||
if (recordsA.Length == 0) return 0;
|
||||
if (!records.Any()) return 0;
|
||||
|
||||
return BulkInsertRecordsWithCommands(database, recordsA);
|
||||
return BulkInsertRecordsWithCommands(database, records.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows for specifying custom DB types that are not natively mapped.
|
||||
/// </summary>
|
||||
public struct SpecialDbType : IEquatable<SpecialDbType>
|
||||
{
|
||||
private readonly string _dbType;
|
||||
|
||||
public SpecialDbType(string dbType)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(dbType))
|
||||
{
|
||||
throw new ArgumentException($"'{nameof(dbType)}' cannot be null or whitespace.", nameof(dbType));
|
||||
}
|
||||
|
||||
_dbType = dbType;
|
||||
}
|
||||
|
||||
public SpecialDbType(SpecialDbTypes specialDbTypes)
|
||||
=> _dbType = specialDbTypes.ToString();
|
||||
|
||||
public static SpecialDbType NTEXT { get; } = new SpecialDbType(SpecialDbTypes.NTEXT);
|
||||
public static SpecialDbType NCHAR { get; } = new SpecialDbType(SpecialDbTypes.NCHAR);
|
||||
public static SpecialDbType NVARCHARMAX { get; } = new SpecialDbType(SpecialDbTypes.NVARCHARMAX);
|
||||
|
||||
public override bool Equals(object obj) => obj is SpecialDbType types && Equals(types);
|
||||
public bool Equals(SpecialDbType other) => _dbType == other._dbType;
|
||||
public override int GetHashCode() => 1038481724 + EqualityComparer<string>.Default.GetHashCode(_dbType);
|
||||
|
||||
public override string ToString() => _dbType.ToString();
|
||||
|
||||
// Make this directly castable to string
|
||||
public static implicit operator string(SpecialDbType dbType) => dbType.ToString();
|
||||
|
||||
// direct equality operators with SpecialDbTypes enum
|
||||
public static bool operator ==(SpecialDbTypes x, SpecialDbType y) => x.ToString() == y;
|
||||
public static bool operator !=(SpecialDbTypes x, SpecialDbType y) => x.ToString() != y;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations
|
||||
{
|
||||
@@ -12,13 +12,14 @@ namespace Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations
|
||||
public class SpecialDbTypeAttribute : Attribute
|
||||
{
|
||||
public SpecialDbTypeAttribute(SpecialDbTypes databaseType)
|
||||
{
|
||||
DatabaseType = databaseType;
|
||||
}
|
||||
=> DatabaseType = new SpecialDbType(databaseType);
|
||||
|
||||
public SpecialDbTypeAttribute(string databaseType)
|
||||
=> DatabaseType = new SpecialDbType(databaseType);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="SpecialDbTypes"/> for this column
|
||||
/// Gets or sets the <see cref="SpecialDbType"/> for this column
|
||||
/// </summary>
|
||||
public SpecialDbTypes DatabaseType { get; private set; }
|
||||
public SpecialDbType DatabaseType { get; private set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
namespace Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations
|
||||
{
|
||||
/// <summary>
|
||||
/// Enum with the two special types that has to be supported because
|
||||
/// of the current umbraco db schema.
|
||||
/// Known special DB types required for Umbraco.
|
||||
/// </summary>
|
||||
public enum SpecialDbTypes
|
||||
{
|
||||
NTEXT,
|
||||
NCHAR,
|
||||
NVARCHARMAX
|
||||
NVARCHARMAX,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Data;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations;
|
||||
|
||||
@@ -12,9 +12,12 @@ namespace Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions
|
||||
//When DbType isn't set explicitly the Type will be used to find the right DbType in the SqlSyntaxProvider.
|
||||
//This type is typically used as part of an initial table creation
|
||||
public Type PropertyType { get; set; }
|
||||
//Only used for special cases as part of an initial table creation
|
||||
public bool HasSpecialDbType { get; set; }
|
||||
public SpecialDbTypes DbType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used for column types that cannot be natively mapped.
|
||||
/// </summary>
|
||||
public SpecialDbType? CustomDbType { get; set; }
|
||||
|
||||
public virtual int Seeding { get; set; }
|
||||
public virtual int Size { get; set; }
|
||||
public virtual int Precision { get; set; }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using NPoco;
|
||||
@@ -75,8 +75,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions
|
||||
var databaseTypeAttribute = propertyInfo.FirstAttribute<SpecialDbTypeAttribute>();
|
||||
if (databaseTypeAttribute != null)
|
||||
{
|
||||
definition.HasSpecialDbType = true;
|
||||
definition.DbType = databaseTypeAttribute.DatabaseType;
|
||||
definition.CustomDbType = databaseTypeAttribute.DatabaseType;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -24,15 +24,16 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
IEnumerable<IProviderSpecificMapperFactory> providerSpecificMapperFactories)
|
||||
{
|
||||
_getFactory = getFactory;
|
||||
_embeddedDatabaseCreators = embeddedDatabaseCreators.ToDictionary(x=>x.ProviderName);
|
||||
_syntaxProviders = syntaxProviders.ToDictionary(x=>x.ProviderName);
|
||||
_bulkSqlInsertProviders = bulkSqlInsertProviders.ToDictionary(x=>x.ProviderName);
|
||||
_providerSpecificMapperFactories = providerSpecificMapperFactories.ToDictionary(x=>x.ProviderName);
|
||||
_embeddedDatabaseCreators = embeddedDatabaseCreators.ToDictionary(x => x.ProviderName);
|
||||
_syntaxProviders = syntaxProviders.ToDictionary(x => x.ProviderName);
|
||||
_bulkSqlInsertProviders = bulkSqlInsertProviders.ToDictionary(x => x.ProviderName);
|
||||
_providerSpecificMapperFactories = providerSpecificMapperFactories.ToDictionary(x => x.ProviderName);
|
||||
}
|
||||
|
||||
public DbProviderFactory CreateFactory(string providerName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(providerName)) return null;
|
||||
if (string.IsNullOrEmpty(providerName))
|
||||
return null;
|
||||
return _getFactory(providerName);
|
||||
}
|
||||
|
||||
@@ -40,7 +41,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
public ISqlSyntaxProvider GetSqlSyntaxProvider(string providerName)
|
||||
{
|
||||
|
||||
if(!_syntaxProviders.TryGetValue(providerName, out var result))
|
||||
if (!_syntaxProviders.TryGetValue(providerName, out var result))
|
||||
{
|
||||
throw new InvalidOperationException($"Unknown provider name \"{providerName}\"");
|
||||
}
|
||||
@@ -51,7 +52,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
public IBulkSqlInsertProvider CreateBulkSqlInsertProvider(string providerName)
|
||||
{
|
||||
|
||||
if(!_bulkSqlInsertProviders.TryGetValue(providerName, out var result))
|
||||
if (!_bulkSqlInsertProviders.TryGetValue(providerName, out var result))
|
||||
{
|
||||
return new BasicBulkSqlInsertProvider();
|
||||
}
|
||||
@@ -61,7 +62,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
|
||||
public void CreateDatabase(string providerName)
|
||||
{
|
||||
if(_embeddedDatabaseCreators.TryGetValue(providerName, out var creator))
|
||||
if (_embeddedDatabaseCreators.TryGetValue(providerName, out var creator))
|
||||
{
|
||||
creator.Create();
|
||||
}
|
||||
@@ -69,7 +70,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
|
||||
public NPocoMapperCollection ProviderSpecificMappers(string providerName)
|
||||
{
|
||||
if(_providerSpecificMapperFactories.TryGetValue(providerName, out var mapperFactory))
|
||||
if (_providerSpecificMapperFactories.TryGetValue(providerName, out var mapperFactory))
|
||||
{
|
||||
return mapperFactory.Mappers;
|
||||
}
|
||||
|
||||
@@ -24,12 +24,18 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos
|
||||
[Index(IndexTypes.NonClustered)]
|
||||
public int UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used to store the name of the provider (i.e. Facebook, Google)
|
||||
/// </summary>
|
||||
[Column("loginProvider")]
|
||||
[Length(4000)] // TODO: This value seems WAY too high, this is just a name
|
||||
[Length(400)]
|
||||
[NullSetting(NullSetting = NullSettings.NotNull)]
|
||||
[Index(IndexTypes.UniqueNonClustered, Name = "IX_" + TableName + "_LoginProvider")]
|
||||
[Index(IndexTypes.UniqueNonClustered, ForColumns = "loginProvider,userId", Name = "IX_" + TableName + "_LoginProvider")]
|
||||
public string LoginProvider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Stores the key the provider uses to lookup the login
|
||||
/// </summary>
|
||||
[Column("providerKey")]
|
||||
[Length(4000)]
|
||||
[NullSetting(NullSetting = NullSettings.NotNull)]
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos
|
||||
internal class PropertyTypeGroupDto
|
||||
{
|
||||
[Column("id")]
|
||||
[PrimaryKeyColumn(IdentitySeed = 12)]
|
||||
[PrimaryKeyColumn(IdentitySeed = 56)]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Column("contenttypeNodeId")]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
@@ -6,6 +6,7 @@ using NPoco;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
{
|
||||
@@ -40,9 +41,10 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
_tableDefinition = DefinitionFactory.GetTableDefinition(pd.Type, sqlSyntaxProvider);
|
||||
if (_tableDefinition == null) throw new InvalidOperationException("No table definition found for type " + pd.Type);
|
||||
|
||||
// only real columns, exclude result columns
|
||||
// only real columns, exclude result/computed columns
|
||||
// Like NPoco does: https://github.com/schotime/NPoco/blob/5117a55fde57547e928246c044fd40bd00b2d7d1/src/NPoco.SqlServer/SqlBulkCopyHelper.cs#L59
|
||||
_readerColumns = pd.Columns
|
||||
.Where(x => x.Value.ResultColumn == false)
|
||||
.Where(x => x.Value.ResultColumn == false && x.Value.ComputedColumn == false)
|
||||
.Select(x => x.Value)
|
||||
.ToArray();
|
||||
|
||||
@@ -68,22 +70,22 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
foreach (var col in _columnDefinitions)
|
||||
{
|
||||
SqlDbType sqlDbType;
|
||||
if (col.HasSpecialDbType)
|
||||
if (col.CustomDbType.HasValue)
|
||||
{
|
||||
//get the SqlDbType from the 'special type'
|
||||
switch (col.DbType)
|
||||
switch (col.CustomDbType)
|
||||
{
|
||||
case SpecialDbTypes.NTEXT:
|
||||
case var x when x == SpecialDbType.NTEXT:
|
||||
sqlDbType = SqlDbType.NText;
|
||||
break;
|
||||
case SpecialDbTypes.NCHAR:
|
||||
case var x when x == SpecialDbType.NCHAR:
|
||||
sqlDbType = SqlDbType.NChar;
|
||||
break;
|
||||
case SpecialDbTypes.NVARCHARMAX:
|
||||
case var x when x == SpecialDbType.NVARCHARMAX:
|
||||
sqlDbType = SqlDbType.NVarChar;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
throw new ArgumentOutOfRangeException("The custom DB type " + col.CustomDbType + " is not supported for bulk import statements.");
|
||||
}
|
||||
}
|
||||
else if (col.Type.HasValue)
|
||||
|
||||
@@ -432,11 +432,12 @@ ORDER BY colName";
|
||||
{
|
||||
var list = new List<string>
|
||||
{
|
||||
"DELETE FROM umbracoUser2UserGroup WHERE userId = @id",
|
||||
"DELETE FROM umbracoUser2NodeNotify WHERE userId = @id",
|
||||
"DELETE FROM umbracoUserStartNode WHERE userId = @id",
|
||||
"DELETE FROM umbracoUser WHERE id = @id",
|
||||
"DELETE FROM umbracoExternalLogin WHERE id = @id"
|
||||
$"DELETE FROM {Constants.DatabaseSchema.Tables.UserLogin} WHERE userId = @id",
|
||||
$"DELETE FROM {Constants.DatabaseSchema.Tables.User2UserGroup} WHERE userId = @id",
|
||||
$"DELETE FROM {Constants.DatabaseSchema.Tables.User2NodeNotify} WHERE userId = @id",
|
||||
$"DELETE FROM {Constants.DatabaseSchema.Tables.UserStartNode} WHERE userId = @id",
|
||||
$"DELETE FROM {Constants.DatabaseSchema.Tables.User} WHERE id = @id",
|
||||
$"DELETE FROM {Constants.DatabaseSchema.Tables.ExternalLogin} WHERE id = @id"
|
||||
};
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,10 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
/// <returns>The number of records that were inserted.</returns>
|
||||
private int BulkInsertRecordsSqlServer<T>(IUmbracoDatabase database, PocoData pocoData, IEnumerable<T> records)
|
||||
{
|
||||
// TODO: The main reason this exists is because the NPoco InsertBulk method doesn't return the number of items.
|
||||
// It is worth investigating the performance of this vs NPoco's because we use a custom BulkDataReader
|
||||
// which in theory should be more efficient than NPocos way of building up an in-memory DataTable.
|
||||
|
||||
// create command against the original database.Connection
|
||||
using (var command = database.CreateCommand(database.Connection, CommandType.Text, string.Empty))
|
||||
{
|
||||
@@ -50,7 +54,13 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
var syntax = database.SqlContext.SqlSyntax as SqlServerSyntaxProvider;
|
||||
if (syntax == null) throw new NotSupportedException("SqlSyntax must be SqlServerSyntaxProvider.");
|
||||
|
||||
using (var copy = new SqlBulkCopy(tConnection, SqlBulkCopyOptions.Default, tTransaction) { BulkCopyTimeout = 10000, DestinationTableName = tableName })
|
||||
using (var copy = new SqlBulkCopy(tConnection, SqlBulkCopyOptions.Default, tTransaction)
|
||||
{
|
||||
BulkCopyTimeout = 0, // 0 = no bulk copy timeout. If a timeout occurs it will be an connection/command timeout.
|
||||
DestinationTableName = tableName,
|
||||
// be consistent with NPoco: https://github.com/schotime/NPoco/blob/5117a55fde57547e928246c044fd40bd00b2d7d1/src/NPoco.SqlServer/SqlBulkCopyHelper.cs#L50
|
||||
BatchSize = 4096
|
||||
})
|
||||
using (var bulkReader = new PocoDataDataReader<T, SqlServerSyntaxProvider>(records, pocoData, syntax))
|
||||
{
|
||||
//we need to add column mappings here because otherwise columns will be matched by their order and if the order of them are different in the DB compared
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
|
||||
@@ -6,24 +6,13 @@ namespace Umbraco.Cms.Infrastructure.Persistence.SqlSyntax
|
||||
{
|
||||
public class DbTypes
|
||||
{
|
||||
public DbType DbType;
|
||||
public string TextDefinition;
|
||||
public bool ShouldQuoteValue;
|
||||
public Dictionary<Type, string> ColumnTypeMap = new Dictionary<Type, string>();
|
||||
public Dictionary<Type, DbType> ColumnDbTypeMap = new Dictionary<Type, DbType>();
|
||||
|
||||
public void Set<T>(DbType dbType, string fieldDefinition)
|
||||
public DbTypes(IReadOnlyDictionary<Type, string> columnTypeMap, IReadOnlyDictionary<Type, DbType> columnDbTypeMap)
|
||||
{
|
||||
DbType = dbType;
|
||||
TextDefinition = fieldDefinition;
|
||||
ShouldQuoteValue = fieldDefinition != "INTEGER"
|
||||
&& fieldDefinition != "BIGINT"
|
||||
&& fieldDefinition != "DOUBLE"
|
||||
&& fieldDefinition != "DECIMAL"
|
||||
&& fieldDefinition != "BOOL";
|
||||
|
||||
ColumnTypeMap[typeof(T)] = fieldDefinition;
|
||||
ColumnDbTypeMap[typeof(T)] = dbType;
|
||||
ColumnTypeMap = columnTypeMap;
|
||||
ColumnDbTypeMap = columnDbTypeMap;
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<Type, string> ColumnTypeMap { get; }
|
||||
public IReadOnlyDictionary<Type, DbType> ColumnDbTypeMap { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Persistence.SqlSyntax
|
||||
{
|
||||
internal class DbTypesFactory
|
||||
{
|
||||
private readonly Dictionary<Type, string> _columnTypeMap = new Dictionary<Type, string>();
|
||||
private readonly Dictionary<Type, DbType> _columnDbTypeMap = new Dictionary<Type, DbType>();
|
||||
|
||||
public void Set<T>(DbType dbType, string fieldDefinition)
|
||||
{
|
||||
_columnTypeMap[typeof(T)] = fieldDefinition;
|
||||
_columnDbTypeMap[typeof(T)] = dbType;
|
||||
}
|
||||
|
||||
public DbTypes Create() => new DbTypes(_columnTypeMap, _columnDbTypeMap);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -28,7 +28,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.SqlSyntax
|
||||
string GetQuotedName(string name);
|
||||
bool DoesTableExist(IDatabase db, string tableName);
|
||||
string GetIndexType(IndexTypes indexTypes);
|
||||
string GetSpecialDbType(SpecialDbTypes dbTypes);
|
||||
string GetSpecialDbType(SpecialDbType dbType);
|
||||
string CreateTable { get; }
|
||||
string DropTable { get; }
|
||||
string AddColumn { get; }
|
||||
|
||||
@@ -22,8 +22,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.SqlSyntax
|
||||
DecimalColumnDefinition = "DECIMAL(38,6)";
|
||||
TimeColumnDefinition = "TIME"; //SQLSERVER 2008+
|
||||
BlobColumnDefinition = "VARBINARY(MAX)";
|
||||
|
||||
InitColumnTypeMap();
|
||||
}
|
||||
|
||||
public override string RenameTable => "sp_rename '{0}', '{1}'";
|
||||
@@ -78,7 +76,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.SqlSyntax
|
||||
/// <returns></returns>
|
||||
public virtual SqlDbType GetSqlDbType(Type clrType)
|
||||
{
|
||||
var dbType = DbTypeMap.ColumnDbTypeMap.First(x => x.Key == clrType).Value;
|
||||
var dbType = DbTypeMap.ColumnDbTypeMap[clrType];
|
||||
return GetSqlDbType(dbType);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace Umbraco.Cms.Infrastructure.Persistence.SqlSyntax
|
||||
public abstract class SqlSyntaxProviderBase<TSyntax> : ISqlSyntaxProvider
|
||||
where TSyntax : ISqlSyntaxProvider
|
||||
{
|
||||
private readonly Lazy<DbTypes> _dbTypes;
|
||||
|
||||
protected SqlSyntaxProviderBase()
|
||||
{
|
||||
ClauseOrder = new List<Func<ColumnDefinition, string>>
|
||||
@@ -42,94 +44,96 @@ namespace Umbraco.Cms.Infrastructure.Persistence.SqlSyntax
|
||||
StringColumnDefinition = string.Format(StringLengthColumnDefinitionFormat, DefaultStringLength);
|
||||
DecimalColumnDefinition = string.Format(DecimalColumnDefinitionFormat, DefaultDecimalPrecision, DefaultDecimalScale);
|
||||
|
||||
InitColumnTypeMap();
|
||||
|
||||
// ReSharper disable VirtualMemberCallInConstructor
|
||||
// ok to call virtual GetQuotedXxxName here - they don't depend on any state
|
||||
var col = Regex.Escape(GetQuotedColumnName("column")).Replace("column", @"\w+");
|
||||
var fld = Regex.Escape(GetQuotedTableName("table") + ".").Replace("table", @"\w+") + col;
|
||||
// ReSharper restore VirtualMemberCallInConstructor
|
||||
AliasRegex = new Regex("(" + fld + @")\s+AS\s+(" + col + ")", RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
_dbTypes = new Lazy<DbTypes>(InitColumnTypeMap);
|
||||
}
|
||||
|
||||
public Regex AliasRegex { get; }
|
||||
|
||||
public string GetWildcardPlaceholder()
|
||||
{
|
||||
return "%";
|
||||
}
|
||||
public string GetWildcardPlaceholder() => "%";
|
||||
|
||||
public string StringLengthNonUnicodeColumnDefinitionFormat = "VARCHAR({0})";
|
||||
public string StringLengthUnicodeColumnDefinitionFormat = "NVARCHAR({0})";
|
||||
public string DecimalColumnDefinitionFormat = "DECIMAL({0},{1})";
|
||||
public string StringLengthNonUnicodeColumnDefinitionFormat { get; } = "VARCHAR({0})";
|
||||
public string StringLengthUnicodeColumnDefinitionFormat { get; } = "NVARCHAR({0})";
|
||||
public string DecimalColumnDefinitionFormat { get; } = "DECIMAL({0},{1})";
|
||||
|
||||
public string DefaultValueFormat = "DEFAULT ({0})";
|
||||
public int DefaultStringLength = 255;
|
||||
public int DefaultDecimalPrecision = 20;
|
||||
public int DefaultDecimalScale = 9;
|
||||
public string DefaultValueFormat { get; } = "DEFAULT ({0})";
|
||||
public int DefaultStringLength { get; } = 255;
|
||||
public int DefaultDecimalPrecision { get; } = 20;
|
||||
public int DefaultDecimalScale { get; } = 9;
|
||||
|
||||
//Set by Constructor
|
||||
public string StringColumnDefinition;
|
||||
public string StringLengthColumnDefinitionFormat;
|
||||
public string StringColumnDefinition { get; }
|
||||
public string StringLengthColumnDefinitionFormat { get; }
|
||||
|
||||
public string AutoIncrementDefinition = "AUTOINCREMENT";
|
||||
public string IntColumnDefinition = "INTEGER";
|
||||
public string LongColumnDefinition = "BIGINT";
|
||||
public string GuidColumnDefinition = "GUID";
|
||||
public string BoolColumnDefinition = "BOOL";
|
||||
public string RealColumnDefinition = "DOUBLE";
|
||||
public string DecimalColumnDefinition;
|
||||
public string BlobColumnDefinition = "BLOB";
|
||||
public string DateTimeColumnDefinition = "DATETIME";
|
||||
public string TimeColumnDefinition = "DATETIME";
|
||||
public string AutoIncrementDefinition { get; protected set; } = "AUTOINCREMENT";
|
||||
public string IntColumnDefinition { get; protected set; } = "INTEGER";
|
||||
public string LongColumnDefinition { get; protected set; } = "BIGINT";
|
||||
public string GuidColumnDefinition { get; protected set; } = "GUID";
|
||||
public string BoolColumnDefinition { get; protected set; } = "BOOL";
|
||||
public string RealColumnDefinition { get; protected set; } = "DOUBLE";
|
||||
public string DecimalColumnDefinition { get; protected set; }
|
||||
public string BlobColumnDefinition { get; protected set; } = "BLOB";
|
||||
public string DateTimeColumnDefinition { get; protected set; } = "DATETIME";
|
||||
public string DateTimeOffsetColumnDefinition { get; protected set; } = "DATETIMEOFFSET(7)";
|
||||
public string TimeColumnDefinition { get; protected set; } = "DATETIME";
|
||||
|
||||
protected IList<Func<ColumnDefinition, string>> ClauseOrder { get; }
|
||||
|
||||
protected DbTypes DbTypeMap = new DbTypes();
|
||||
protected void InitColumnTypeMap()
|
||||
protected DbTypes DbTypeMap => _dbTypes.Value;
|
||||
|
||||
private DbTypes InitColumnTypeMap()
|
||||
{
|
||||
DbTypeMap.Set<string>(DbType.String, StringColumnDefinition);
|
||||
DbTypeMap.Set<char>(DbType.StringFixedLength, StringColumnDefinition);
|
||||
DbTypeMap.Set<char?>(DbType.StringFixedLength, StringColumnDefinition);
|
||||
DbTypeMap.Set<char[]>(DbType.String, StringColumnDefinition);
|
||||
DbTypeMap.Set<bool>(DbType.Boolean, BoolColumnDefinition);
|
||||
DbTypeMap.Set<bool?>(DbType.Boolean, BoolColumnDefinition);
|
||||
DbTypeMap.Set<Guid>(DbType.Guid, GuidColumnDefinition);
|
||||
DbTypeMap.Set<Guid?>(DbType.Guid, GuidColumnDefinition);
|
||||
DbTypeMap.Set<DateTime>(DbType.DateTime, DateTimeColumnDefinition);
|
||||
DbTypeMap.Set<DateTime?>(DbType.DateTime, DateTimeColumnDefinition);
|
||||
DbTypeMap.Set<TimeSpan>(DbType.Time, TimeColumnDefinition);
|
||||
DbTypeMap.Set<TimeSpan?>(DbType.Time, TimeColumnDefinition);
|
||||
DbTypeMap.Set<DateTimeOffset>(DbType.Time, TimeColumnDefinition);
|
||||
DbTypeMap.Set<DateTimeOffset?>(DbType.Time, TimeColumnDefinition);
|
||||
var dbTypeMap = new DbTypesFactory();
|
||||
dbTypeMap.Set<string>(DbType.String, StringColumnDefinition);
|
||||
dbTypeMap.Set<char>(DbType.StringFixedLength, StringColumnDefinition);
|
||||
dbTypeMap.Set<char?>(DbType.StringFixedLength, StringColumnDefinition);
|
||||
dbTypeMap.Set<char[]>(DbType.String, StringColumnDefinition);
|
||||
dbTypeMap.Set<bool>(DbType.Boolean, BoolColumnDefinition);
|
||||
dbTypeMap.Set<bool?>(DbType.Boolean, BoolColumnDefinition);
|
||||
dbTypeMap.Set<Guid>(DbType.Guid, GuidColumnDefinition);
|
||||
dbTypeMap.Set<Guid?>(DbType.Guid, GuidColumnDefinition);
|
||||
dbTypeMap.Set<DateTime>(DbType.DateTime, DateTimeColumnDefinition);
|
||||
dbTypeMap.Set<DateTime?>(DbType.DateTime, DateTimeColumnDefinition);
|
||||
dbTypeMap.Set<TimeSpan>(DbType.Time, TimeColumnDefinition);
|
||||
dbTypeMap.Set<TimeSpan?>(DbType.Time, TimeColumnDefinition);
|
||||
dbTypeMap.Set<DateTimeOffset>(DbType.DateTimeOffset, DateTimeOffsetColumnDefinition);
|
||||
dbTypeMap.Set<DateTimeOffset?>(DbType.DateTimeOffset, DateTimeOffsetColumnDefinition);
|
||||
|
||||
DbTypeMap.Set<byte>(DbType.Byte, IntColumnDefinition);
|
||||
DbTypeMap.Set<byte?>(DbType.Byte, IntColumnDefinition);
|
||||
DbTypeMap.Set<sbyte>(DbType.SByte, IntColumnDefinition);
|
||||
DbTypeMap.Set<sbyte?>(DbType.SByte, IntColumnDefinition);
|
||||
DbTypeMap.Set<short>(DbType.Int16, IntColumnDefinition);
|
||||
DbTypeMap.Set<short?>(DbType.Int16, IntColumnDefinition);
|
||||
DbTypeMap.Set<ushort>(DbType.UInt16, IntColumnDefinition);
|
||||
DbTypeMap.Set<ushort?>(DbType.UInt16, IntColumnDefinition);
|
||||
DbTypeMap.Set<int>(DbType.Int32, IntColumnDefinition);
|
||||
DbTypeMap.Set<int?>(DbType.Int32, IntColumnDefinition);
|
||||
DbTypeMap.Set<uint>(DbType.UInt32, IntColumnDefinition);
|
||||
DbTypeMap.Set<uint?>(DbType.UInt32, IntColumnDefinition);
|
||||
dbTypeMap.Set<byte>(DbType.Byte, IntColumnDefinition);
|
||||
dbTypeMap.Set<byte?>(DbType.Byte, IntColumnDefinition);
|
||||
dbTypeMap.Set<sbyte>(DbType.SByte, IntColumnDefinition);
|
||||
dbTypeMap.Set<sbyte?>(DbType.SByte, IntColumnDefinition);
|
||||
dbTypeMap.Set<short>(DbType.Int16, IntColumnDefinition);
|
||||
dbTypeMap.Set<short?>(DbType.Int16, IntColumnDefinition);
|
||||
dbTypeMap.Set<ushort>(DbType.UInt16, IntColumnDefinition);
|
||||
dbTypeMap.Set<ushort?>(DbType.UInt16, IntColumnDefinition);
|
||||
dbTypeMap.Set<int>(DbType.Int32, IntColumnDefinition);
|
||||
dbTypeMap.Set<int?>(DbType.Int32, IntColumnDefinition);
|
||||
dbTypeMap.Set<uint>(DbType.UInt32, IntColumnDefinition);
|
||||
dbTypeMap.Set<uint?>(DbType.UInt32, IntColumnDefinition);
|
||||
|
||||
DbTypeMap.Set<long>(DbType.Int64, LongColumnDefinition);
|
||||
DbTypeMap.Set<long?>(DbType.Int64, LongColumnDefinition);
|
||||
DbTypeMap.Set<ulong>(DbType.UInt64, LongColumnDefinition);
|
||||
DbTypeMap.Set<ulong?>(DbType.UInt64, LongColumnDefinition);
|
||||
dbTypeMap.Set<long>(DbType.Int64, LongColumnDefinition);
|
||||
dbTypeMap.Set<long?>(DbType.Int64, LongColumnDefinition);
|
||||
dbTypeMap.Set<ulong>(DbType.UInt64, LongColumnDefinition);
|
||||
dbTypeMap.Set<ulong?>(DbType.UInt64, LongColumnDefinition);
|
||||
|
||||
DbTypeMap.Set<float>(DbType.Single, RealColumnDefinition);
|
||||
DbTypeMap.Set<float?>(DbType.Single, RealColumnDefinition);
|
||||
DbTypeMap.Set<double>(DbType.Double, RealColumnDefinition);
|
||||
DbTypeMap.Set<double?>(DbType.Double, RealColumnDefinition);
|
||||
dbTypeMap.Set<float>(DbType.Single, RealColumnDefinition);
|
||||
dbTypeMap.Set<float?>(DbType.Single, RealColumnDefinition);
|
||||
dbTypeMap.Set<double>(DbType.Double, RealColumnDefinition);
|
||||
dbTypeMap.Set<double?>(DbType.Double, RealColumnDefinition);
|
||||
|
||||
DbTypeMap.Set<decimal>(DbType.Decimal, DecimalColumnDefinition);
|
||||
DbTypeMap.Set<decimal?>(DbType.Decimal, DecimalColumnDefinition);
|
||||
dbTypeMap.Set<decimal>(DbType.Decimal, DecimalColumnDefinition);
|
||||
dbTypeMap.Set<decimal?>(DbType.Decimal, DecimalColumnDefinition);
|
||||
|
||||
DbTypeMap.Set<byte[]>(DbType.Binary, BlobColumnDefinition);
|
||||
dbTypeMap.Set<byte[]>(DbType.Binary, BlobColumnDefinition);
|
||||
|
||||
return dbTypeMap.Create();
|
||||
}
|
||||
|
||||
public abstract string ProviderName { get; }
|
||||
@@ -193,17 +197,17 @@ namespace Umbraco.Cms.Infrastructure.Persistence.SqlSyntax
|
||||
return indexType;
|
||||
}
|
||||
|
||||
public virtual string GetSpecialDbType(SpecialDbTypes dbTypes)
|
||||
public virtual string GetSpecialDbType(SpecialDbType dbType)
|
||||
{
|
||||
if (dbTypes == SpecialDbTypes.NCHAR)
|
||||
if (dbType == SpecialDbType.NCHAR)
|
||||
{
|
||||
return "NCHAR";
|
||||
return SpecialDbType.NCHAR;
|
||||
}
|
||||
else if (dbTypes == SpecialDbTypes.NTEXT)
|
||||
else if (dbType == SpecialDbType.NTEXT)
|
||||
{
|
||||
return "NTEXT";
|
||||
return SpecialDbType.NTEXT;
|
||||
}
|
||||
else if (dbTypes == SpecialDbTypes.NVARCHARMAX)
|
||||
else if (dbType == SpecialDbType.NVARCHARMAX)
|
||||
{
|
||||
return "NVARCHAR(MAX)";
|
||||
}
|
||||
@@ -470,14 +474,14 @@ namespace Umbraco.Cms.Infrastructure.Persistence.SqlSyntax
|
||||
if (column.Type.HasValue == false && string.IsNullOrEmpty(column.CustomType) == false)
|
||||
return column.CustomType;
|
||||
|
||||
if (column.HasSpecialDbType)
|
||||
if (column.CustomDbType.HasValue)
|
||||
{
|
||||
if (column.Size != default(int))
|
||||
if (column.Size != default)
|
||||
{
|
||||
return $"{GetSpecialDbType(column.DbType)}({column.Size})";
|
||||
return $"{GetSpecialDbType(column.CustomDbType.Value)}({column.Size})";
|
||||
}
|
||||
|
||||
return GetSpecialDbType(column.DbType);
|
||||
return GetSpecialDbType(column.CustomDbType.Value);
|
||||
}
|
||||
|
||||
var type = column.Type.HasValue
|
||||
@@ -486,19 +490,19 @@ namespace Umbraco.Cms.Infrastructure.Persistence.SqlSyntax
|
||||
|
||||
if (type == typeof(string))
|
||||
{
|
||||
var valueOrDefault = column.Size != default(int) ? column.Size : DefaultStringLength;
|
||||
var valueOrDefault = column.Size != default ? column.Size : DefaultStringLength;
|
||||
return string.Format(StringLengthColumnDefinitionFormat, valueOrDefault);
|
||||
}
|
||||
|
||||
if (type == typeof(decimal))
|
||||
{
|
||||
var precision = column.Size != default(int) ? column.Size : DefaultDecimalPrecision;
|
||||
var scale = column.Precision != default(int) ? column.Precision : DefaultDecimalScale;
|
||||
var precision = column.Size != default ? column.Size : DefaultDecimalPrecision;
|
||||
var scale = column.Precision != default ? column.Precision : DefaultDecimalScale;
|
||||
return string.Format(DecimalColumnDefinitionFormat, precision, scale);
|
||||
}
|
||||
|
||||
var definition = DbTypeMap.ColumnTypeMap.First(x => x.Key == type).Value;
|
||||
var dbTypeDefinition = column.Size != default(int)
|
||||
var definition = DbTypeMap.ColumnTypeMap[type];
|
||||
var dbTypeDefinition = column.Size != default
|
||||
? $"{definition}({column.Size})"
|
||||
: definition;
|
||||
//NOTE Precision is left out
|
||||
|
||||
@@ -5,7 +5,7 @@ using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Cms.Core.Routing;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
@@ -29,6 +30,7 @@ namespace Umbraco.Cms.Core.Routing
|
||||
INotificationHandler<ContentMovingNotification>,
|
||||
INotificationHandler<ContentMovedNotification>
|
||||
{
|
||||
private readonly ILogger<RedirectTrackingHandler> _logger;
|
||||
private readonly IOptionsMonitor<WebRoutingSettings> _webRoutingSettings;
|
||||
private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor;
|
||||
private readonly IRedirectUrlService _redirectUrlService;
|
||||
@@ -36,8 +38,14 @@ namespace Umbraco.Cms.Core.Routing
|
||||
|
||||
private const string NotificationStateKey = "Umbraco.Cms.Core.Routing.RedirectTrackingHandler";
|
||||
|
||||
public RedirectTrackingHandler(IOptionsMonitor<WebRoutingSettings> webRoutingSettings, IPublishedSnapshotAccessor publishedSnapshotAccessor, IRedirectUrlService redirectUrlService, IVariationContextAccessor variationContextAccessor)
|
||||
public RedirectTrackingHandler(
|
||||
ILogger<RedirectTrackingHandler> logger,
|
||||
IOptionsMonitor<WebRoutingSettings> webRoutingSettings,
|
||||
IPublishedSnapshotAccessor publishedSnapshotAccessor,
|
||||
IRedirectUrlService redirectUrlService,
|
||||
IVariationContextAccessor variationContextAccessor)
|
||||
{
|
||||
_logger = logger;
|
||||
_webRoutingSettings = webRoutingSettings;
|
||||
_publishedSnapshotAccessor = publishedSnapshotAccessor;
|
||||
_redirectUrlService = redirectUrlService;
|
||||
@@ -87,8 +95,8 @@ namespace Umbraco.Cms.Core.Routing
|
||||
|
||||
private void StoreOldRoute(IContent entity, OldRoutesDictionary oldRoutes)
|
||||
{
|
||||
var contentCache = _publishedSnapshotAccessor.PublishedSnapshot.Content;
|
||||
var entityContent = contentCache.GetById(entity.Id);
|
||||
var contentCache = _publishedSnapshotAccessor.PublishedSnapshot?.Content;
|
||||
var entityContent = contentCache?.GetById(entity.Id);
|
||||
if (entityContent == null)
|
||||
return;
|
||||
|
||||
@@ -112,7 +120,13 @@ namespace Umbraco.Cms.Core.Routing
|
||||
|
||||
private void CreateRedirects(OldRoutesDictionary oldRoutes)
|
||||
{
|
||||
var contentCache = _publishedSnapshotAccessor.PublishedSnapshot.Content;
|
||||
var contentCache = _publishedSnapshotAccessor.PublishedSnapshot?.Content;
|
||||
|
||||
if (contentCache == null)
|
||||
{
|
||||
_logger.LogWarning("Could not track redirects because there is no current published snapshot available.");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var oldRoute in oldRoutes)
|
||||
{
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace Umbraco.Cms.Core.Security
|
||||
private string[] _allowedSections;
|
||||
private int[] _startMediaIds;
|
||||
private int[] _startContentIds;
|
||||
private DateTime? _inviteDateUtc;
|
||||
|
||||
private static readonly DelegateEqualityComparer<int[]> s_startIdsComparer = new DelegateEqualityComparer<int[]>(
|
||||
(groups, enumerable) => groups.UnsortedSequenceEqual(enumerable),
|
||||
@@ -75,6 +76,15 @@ namespace Umbraco.Cms.Core.Security
|
||||
public int[] CalculatedMediaStartNodeIds { get; set; }
|
||||
public int[] CalculatedContentStartNodeIds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets invite date
|
||||
/// </summary>
|
||||
public DateTime? InviteDateUtc
|
||||
{
|
||||
get => _inviteDateUtc;
|
||||
set => BeingDirty.SetPropertyValueAndDetectChanges(value, ref _inviteDateUtc, nameof(InviteDateUtc));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets content start nodes assigned to the User (not ones assigned to the user's groups)
|
||||
/// </summary>
|
||||
|
||||
@@ -438,6 +438,13 @@ namespace Umbraco.Cms.Core.Security
|
||||
user.LastLoginDate = dt;
|
||||
}
|
||||
|
||||
if (identityUser.IsPropertyDirty(nameof(BackOfficeIdentityUser.InviteDateUtc))
|
||||
|| (user.InvitedDate?.ToUniversalTime() != identityUser.InviteDateUtc))
|
||||
{
|
||||
anythingChanged = true;
|
||||
user.InvitedDate = identityUser.InviteDateUtc?.ToLocalTime();
|
||||
}
|
||||
|
||||
if (identityUser.IsPropertyDirty(nameof(BackOfficeIdentityUser.LastPasswordChangeDateUtc))
|
||||
|| (user.LastPasswordChangeDate != default && identityUser.LastPasswordChangeDateUtc.HasValue == false)
|
||||
|| (identityUser.LastPasswordChangeDateUtc.HasValue && user.LastPasswordChangeDate.ToUniversalTime() != identityUser.LastPasswordChangeDateUtc.Value))
|
||||
|
||||
@@ -74,6 +74,7 @@ namespace Umbraco.Cms.Core.Security
|
||||
target.UserName = source.Username;
|
||||
target.LastPasswordChangeDateUtc = source.LastPasswordChangeDate.ToUniversalTime();
|
||||
target.LastLoginDateUtc = source.LastLoginDate.ToUniversalTime();
|
||||
target.InviteDateUtc = source.InvitedDate?.ToUniversalTime();
|
||||
target.EmailConfirmed = source.EmailConfirmedDate.HasValue;
|
||||
target.Name = source.Name;
|
||||
target.AccessFailedCount = source.FailedPasswordAttempts;
|
||||
@@ -87,7 +88,7 @@ namespace Umbraco.Cms.Core.Security
|
||||
target.LockoutEnd = source.IsLockedOut ? DateTime.MaxValue.ToUniversalTime() : (DateTime?)null;
|
||||
}
|
||||
|
||||
// TODO: We need to validate this mapping is OK, we need to get Umbraco.Code working
|
||||
// Umbraco.Code.MapAll -Id -LockoutEnabled -PhoneNumber -PhoneNumberConfirmed -TwoFactorEnabled -ConcurrencyStamp -NormalizedEmail -NormalizedUserName -Roles
|
||||
private void Map(IMember source, MemberIdentityUser target)
|
||||
{
|
||||
target.Email = source.Email;
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Net;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
|
||||
namespace Umbraco.Cms.Core.Services.Implement
|
||||
{
|
||||
public class BasicAuthService : IBasicAuthService
|
||||
{
|
||||
private BasicAuthSettings _basicAuthSettings;
|
||||
|
||||
public BasicAuthService(IOptionsMonitor<BasicAuthSettings> optionsMonitor)
|
||||
{
|
||||
_basicAuthSettings = optionsMonitor.CurrentValue;
|
||||
|
||||
optionsMonitor.OnChange(basicAuthSettings => _basicAuthSettings = basicAuthSettings);
|
||||
}
|
||||
|
||||
public bool IsBasicAuthEnabled() => _basicAuthSettings.Enabled;
|
||||
|
||||
public bool IsIpAllowListed(IPAddress clientIpAddress)
|
||||
{
|
||||
foreach (var allowedIpString in _basicAuthSettings.AllowedIPs)
|
||||
{
|
||||
if (IPNetwork.TryParse(allowedIpString, out IPNetwork allowedIp) && allowedIp.Contains(clientIpAddress))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,15 +19,16 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.34" />
|
||||
<PackageReference Include="MailKit" Version="2.13.0" />
|
||||
<PackageReference Include="MailKit" Version="2.14.0" />
|
||||
<PackageReference Include="IPNetwork2" Version="2.5.329" />
|
||||
<PackageReference Include="Markdown" Version="2.2.1" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" />
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="5.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="5.0.8" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" />
|
||||
<PackageReference Include="MiniProfiler.Shared" Version="4.2.22" />
|
||||
<PackageReference Include="ncrontab" Version="3.3.1" />
|
||||
@@ -40,12 +41,12 @@
|
||||
<PackageReference Include="Serilog.Filters.Expressions" Version="2.1.0" />
|
||||
<PackageReference Include="Serilog.Formatting.Compact" Version="1.1.0" />
|
||||
<PackageReference Include="Serilog.Formatting.Compact.Reader" Version="1.0.5" />
|
||||
<PackageReference Include="Serilog.Settings.AppSettings" Version="2.2.2" />
|
||||
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Async" Version="1.4.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Map" Version="1.0.2" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.3" />
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="5.0.0" />
|
||||
<PackageReference Include="System.Data.SqlClient" Version="4.8.2" />
|
||||
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="5.0.0" />
|
||||
<PackageReference Include="System.Text.Encodings.Web" Version="5.0.1" /> <!-- Explicit updated this nested dependency due to this https://github.com/dotnet/announcements/issues/178-->
|
||||
|
||||
@@ -21,6 +21,8 @@ namespace Umbraco.Cms.Infrastructure.WebAssets
|
||||
public const string UmbracoInitCssBundleName = "umbraco-backoffice-init-css";
|
||||
public const string UmbracoCoreJsBundleName = "umbraco-backoffice-js";
|
||||
public const string UmbracoExtensionsJsBundleName = "umbraco-backoffice-extensions-js";
|
||||
public const string UmbracoNonOptimizedPackageJsBundleName = "umbraco-backoffice-non-optimized-js";
|
||||
public const string UmbracoNonOptimizedPackageCssBundleName = "umbraco-backoffice-non-optimized-css";
|
||||
public const string UmbracoTinyMceJsBundleName = "umbraco-tinymce-js";
|
||||
public const string UmbracoUpgradeCssBundleName = "umbraco-authorize-upgrade-css";
|
||||
|
||||
@@ -51,47 +53,134 @@ namespace Umbraco.Cms.Infrastructure.WebAssets
|
||||
{
|
||||
// Create bundles
|
||||
|
||||
_runtimeMinifier.CreateCssBundle(UmbracoInitCssBundleName, false,
|
||||
FormatPaths("lib/bootstrap-social/bootstrap-social.css",
|
||||
"assets/css/umbraco.min.css",
|
||||
"lib/font-awesome/css/font-awesome.min.css"));
|
||||
_runtimeMinifier.CreateCssBundle(UmbracoInitCssBundleName,
|
||||
BundlingOptions.NotOptimizedAndComposite,
|
||||
FormatPaths(
|
||||
"assets/css/umbraco.min.css",
|
||||
"lib/bootstrap-social/bootstrap-social.css",
|
||||
"lib/font-awesome/css/font-awesome.min.css"));
|
||||
|
||||
_runtimeMinifier.CreateCssBundle(UmbracoUpgradeCssBundleName, false,
|
||||
FormatPaths("assets/css/umbraco.min.css",
|
||||
"lib/bootstrap-social/bootstrap-social.css",
|
||||
"lib/font-awesome/css/font-awesome.min.css"));
|
||||
_runtimeMinifier.CreateCssBundle(UmbracoUpgradeCssBundleName,
|
||||
BundlingOptions.NotOptimizedAndComposite,
|
||||
FormatPaths(
|
||||
"assets/css/umbraco.min.css",
|
||||
"lib/bootstrap-social/bootstrap-social.css",
|
||||
"lib/font-awesome/css/font-awesome.min.css"));
|
||||
|
||||
_runtimeMinifier.CreateCssBundle(UmbracoPreviewCssBundleName, false,
|
||||
_runtimeMinifier.CreateCssBundle(UmbracoPreviewCssBundleName,
|
||||
BundlingOptions.NotOptimizedAndComposite,
|
||||
FormatPaths("assets/css/canvasdesigner.min.css"));
|
||||
|
||||
_runtimeMinifier.CreateJsBundle(UmbracoPreviewJsBundleName, false,
|
||||
_runtimeMinifier.CreateJsBundle(UmbracoPreviewJsBundleName,
|
||||
BundlingOptions.NotOptimizedAndComposite,
|
||||
FormatPaths(GetScriptsForPreview()));
|
||||
|
||||
_runtimeMinifier.CreateJsBundle(UmbracoTinyMceJsBundleName, false,
|
||||
_runtimeMinifier.CreateJsBundle(UmbracoTinyMceJsBundleName,
|
||||
BundlingOptions.NotOptimizedAndComposite,
|
||||
FormatPaths(GetScriptsForTinyMce()));
|
||||
|
||||
_runtimeMinifier.CreateJsBundle(UmbracoCoreJsBundleName, false,
|
||||
_runtimeMinifier.CreateJsBundle(UmbracoCoreJsBundleName,
|
||||
BundlingOptions.NotOptimizedAndComposite,
|
||||
FormatPaths(GetScriptsForBackOfficeCore()));
|
||||
|
||||
|
||||
// get the property editor assets
|
||||
var propertyEditorAssets = ScanPropertyEditors()
|
||||
.GroupBy(x => x.AssetType)
|
||||
.ToDictionary(x => x.Key, x => x.Select(c => c.FilePath));
|
||||
|
||||
// get the back office custom assets
|
||||
var customAssets = _customBackOfficeAssetsCollection.GroupBy(x => x.DependencyType).ToDictionary(x => x.Key, x => x.Select(c => c.FilePath));
|
||||
|
||||
var jsAssets = (customAssets.TryGetValue(AssetType.Javascript, out var customScripts) ? customScripts : Enumerable.Empty<string>())
|
||||
.Union(propertyEditorAssets.TryGetValue(AssetType.Javascript, out var scripts) ? scripts : Enumerable.Empty<string>());
|
||||
// This bundle includes all scripts from property editor assets,
|
||||
// custom back office assets, and any scripts found in package manifests
|
||||
// that have the default bundle options.
|
||||
|
||||
IEnumerable<string> jsAssets = (customAssets.TryGetValue(AssetType.Javascript, out IEnumerable<string> customScripts) ? customScripts : Enumerable.Empty<string>())
|
||||
.Union(propertyEditorAssets.TryGetValue(AssetType.Javascript, out IEnumerable<string> scripts) ? scripts : Enumerable.Empty<string>());
|
||||
|
||||
_runtimeMinifier.CreateJsBundle(
|
||||
UmbracoExtensionsJsBundleName, true,
|
||||
UmbracoExtensionsJsBundleName,
|
||||
BundlingOptions.OptimizedAndComposite,
|
||||
FormatPaths(
|
||||
GetScriptsForBackOfficeExtensions(jsAssets)));
|
||||
|
||||
var cssAssets = (customAssets.TryGetValue(AssetType.Css, out var customStyles) ? customStyles : Enumerable.Empty<string>())
|
||||
.Union(propertyEditorAssets.TryGetValue(AssetType.Css, out var styles) ? styles : Enumerable.Empty<string>());
|
||||
// Create a bundle per package manifest that is declaring an Independent bundle type
|
||||
RegisterPackageBundlesForIndependentOptions(_parser.CombinedManifest.Scripts, AssetType.Javascript);
|
||||
|
||||
// Create a single non-optimized (no file processing) bundle for all manifests declaring None as a bundle option
|
||||
RegisterPackageBundlesForNoneOption(_parser.CombinedManifest.Scripts, UmbracoNonOptimizedPackageJsBundleName);
|
||||
|
||||
// This bundle includes all CSS from property editor assets,
|
||||
// custom back office assets, and any CSS found in package manifests
|
||||
// that have the default bundle options.
|
||||
|
||||
IEnumerable<string> cssAssets = (customAssets.TryGetValue(AssetType.Css, out IEnumerable<string> customStyles) ? customStyles : Enumerable.Empty<string>())
|
||||
.Union(propertyEditorAssets.TryGetValue(AssetType.Css, out IEnumerable<string> styles) ? styles : Enumerable.Empty<string>());
|
||||
|
||||
_runtimeMinifier.CreateCssBundle(
|
||||
UmbracoCssBundleName, true,
|
||||
UmbracoCssBundleName,
|
||||
BundlingOptions.OptimizedAndComposite,
|
||||
FormatPaths(
|
||||
GetStylesheetsForBackOffice(cssAssets)));
|
||||
|
||||
// Create a bundle per package manifest that is declaring an Independent bundle type
|
||||
RegisterPackageBundlesForIndependentOptions(_parser.CombinedManifest.Stylesheets, AssetType.Css);
|
||||
|
||||
// Create a single non-optimized (no file processing) bundle for all manifests declaring None as a bundle option
|
||||
RegisterPackageBundlesForNoneOption(_parser.CombinedManifest.Stylesheets, UmbracoNonOptimizedPackageCssBundleName);
|
||||
}
|
||||
|
||||
public static string GetIndependentPackageBundleName(ManifestAssets manifestAssets, AssetType assetType)
|
||||
=> $"{manifestAssets.PackageName.ToLowerInvariant()}-{(assetType == AssetType.Css ? "css" : "js")}";
|
||||
|
||||
private void RegisterPackageBundlesForNoneOption(
|
||||
IReadOnlyDictionary<BundleOptions, IReadOnlyList<ManifestAssets>> combinedPackageManifestAssets,
|
||||
string bundleName)
|
||||
{
|
||||
var assets = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
|
||||
|
||||
// Create a bundle per package manifest that is declaring the matching BundleOptions
|
||||
if (combinedPackageManifestAssets.TryGetValue(BundleOptions.None, out IReadOnlyList<ManifestAssets> manifestAssetList))
|
||||
{
|
||||
foreach(var asset in manifestAssetList.SelectMany(x => x.Assets))
|
||||
{
|
||||
assets.Add(asset);
|
||||
}
|
||||
}
|
||||
|
||||
_runtimeMinifier.CreateJsBundle(
|
||||
bundleName,
|
||||
// no optimization, no composite files, just render individual files
|
||||
BundlingOptions.NotOptimizedNotComposite,
|
||||
FormatPaths(assets.ToArray()));
|
||||
}
|
||||
|
||||
private void RegisterPackageBundlesForIndependentOptions(
|
||||
IReadOnlyDictionary<BundleOptions, IReadOnlyList<ManifestAssets>> combinedPackageManifestAssets,
|
||||
AssetType assetType)
|
||||
{
|
||||
// Create a bundle per package manifest that is declaring the matching BundleOptions
|
||||
if (combinedPackageManifestAssets.TryGetValue(BundleOptions.Independent, out IReadOnlyList<ManifestAssets> manifestAssetList))
|
||||
{
|
||||
foreach (ManifestAssets manifestAssets in manifestAssetList)
|
||||
{
|
||||
string bundleName = GetIndependentPackageBundleName(manifestAssets, assetType);
|
||||
string[] filePaths = FormatPaths(manifestAssets.Assets.ToArray());
|
||||
|
||||
switch (assetType)
|
||||
{
|
||||
case AssetType.Javascript:
|
||||
_runtimeMinifier.CreateJsBundle(bundleName, BundlingOptions.OptimizedAndComposite, filePaths);
|
||||
break;
|
||||
case AssetType.Css:
|
||||
_runtimeMinifier.CreateCssBundle(bundleName, BundlingOptions.OptimizedAndComposite, filePaths);
|
||||
break;
|
||||
default:
|
||||
throw new IndexOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -100,10 +189,15 @@ namespace Umbraco.Cms.Infrastructure.WebAssets
|
||||
/// <returns></returns>
|
||||
private string[] GetScriptsForBackOfficeExtensions(IEnumerable<string> propertyEditorScripts)
|
||||
{
|
||||
var scripts = new HashSet<string>();
|
||||
foreach (string script in _parser.Manifest.Scripts)
|
||||
var scripts = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
|
||||
|
||||
// only include scripts with the default bundle options here
|
||||
if (_parser.CombinedManifest.Scripts.TryGetValue(BundleOptions.Default, out IReadOnlyList<ManifestAssets> manifestAssets))
|
||||
{
|
||||
scripts.Add(script);
|
||||
foreach (string script in manifestAssets.SelectMany(x => x.Assets))
|
||||
{
|
||||
scripts.Add(script);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (string script in propertyEditorScripts)
|
||||
@@ -130,11 +224,15 @@ namespace Umbraco.Cms.Infrastructure.WebAssets
|
||||
/// <returns></returns>
|
||||
private string[] GetStylesheetsForBackOffice(IEnumerable<string> propertyEditorStyles)
|
||||
{
|
||||
var stylesheets = new HashSet<string>();
|
||||
var stylesheets = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
|
||||
|
||||
foreach (string script in _parser.Manifest.Stylesheets)
|
||||
// only include css with the default bundle options here
|
||||
if (_parser.CombinedManifest.Stylesheets.TryGetValue(BundleOptions.Default, out IReadOnlyList<ManifestAssets> manifestAssets))
|
||||
{
|
||||
stylesheets.Add(script);
|
||||
foreach (string script in manifestAssets.SelectMany(x => x.Assets))
|
||||
{
|
||||
stylesheets.Add(script);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (string stylesheet in propertyEditorStyles)
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Hosting;
|
||||
using Umbraco.Cms.Core.WebAssets;
|
||||
using Umbraco.Cms.Infrastructure.WebAssets;
|
||||
|
||||
namespace Umbraco.Extensions
|
||||
{
|
||||
public static class RuntimeMinifierExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the JavaScript to load the back office's assets
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static async Task<string> GetScriptForLoadingBackOfficeAsync(this IRuntimeMinifier minifier, GlobalSettings globalSettings, IHostingEnvironment hostingEnvironment)
|
||||
{
|
||||
var coreScripts = await minifier.GetJsAssetPathsAsync(BackOfficeWebAssets.UmbracoCoreJsBundleName);
|
||||
var extensionsScripts = await minifier.GetJsAssetPathsAsync(BackOfficeWebAssets.UmbracoExtensionsJsBundleName);
|
||||
var result = BackOfficeJavaScriptInitializer.GetJavascriptInitialization(coreScripts.Union(extensionsScripts), "umbraco", globalSettings, hostingEnvironment);
|
||||
result += await GetStylesheetInitializationAsync(minifier);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the back office css bundle paths and formats a JS call to lazy load them
|
||||
/// </summary>
|
||||
private static async Task<string> GetStylesheetInitializationAsync(IRuntimeMinifier minifier)
|
||||
{
|
||||
var files = await minifier.GetCssAssetPathsAsync(BackOfficeWebAssets.UmbracoCssBundleName);
|
||||
var sb = new StringBuilder();
|
||||
foreach (var file in files)
|
||||
sb.AppendFormat("{0}LazyLoad.css('{1}');", Environment.NewLine, file);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -17,13 +17,12 @@ namespace Umbraco.Cms.Persistence.SqlCe
|
||||
|
||||
public int BulkInsertRecords<T>(IUmbracoDatabase database, IEnumerable<T> records)
|
||||
{
|
||||
var recordsA = records.ToArray();
|
||||
if (recordsA.Length == 0) return 0;
|
||||
if (!records.Any()) return 0;
|
||||
|
||||
var pocoData = database.PocoDataFactory.ForType(typeof(T));
|
||||
if (pocoData == null) throw new InvalidOperationException("Could not find PocoData for " + typeof(T));
|
||||
|
||||
return BulkInsertRecordsSqlCe(database, pocoData, recordsA);
|
||||
return BulkInsertRecordsSqlCe(database, pocoData, records.ToArray());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
@@ -25,10 +25,10 @@ namespace Umbraco.Cms.Persistence.SqlCe
|
||||
{
|
||||
_globalSettings = globalSettings;
|
||||
BlobColumnDefinition = "IMAGE";
|
||||
// This is silly to have to do this but the way these inherited classes are structured it's the easiest
|
||||
// way without an overhaul in type map initialization
|
||||
DbTypeMap.Set<byte[]>(DbType.Binary, BlobColumnDefinition);
|
||||
|
||||
// NOTE: if this column type is used in sqlce, it will prob result in errors since
|
||||
// SQLCE cannot support this type correctly without 2x columns and a lot of work arounds.
|
||||
// We don't use this natively within Umbraco but 3rd parties might with SQL server.
|
||||
DateTimeOffsetColumnDefinition = "DATETIME";
|
||||
}
|
||||
|
||||
public override string ProviderName => Constants.DatabaseProviders.SqlCe;
|
||||
@@ -300,10 +300,14 @@ where table_name=@0 and column_name=@1", tableName, columnName).FirstOrDefault()
|
||||
GetQuotedTableName(index.TableName), columns);
|
||||
}
|
||||
|
||||
public override string GetSpecialDbType(SpecialDbTypes dbTypes)
|
||||
public override string GetSpecialDbType(SpecialDbType dbTypes)
|
||||
{
|
||||
if (dbTypes == SpecialDbTypes.NVARCHARMAX) // SqlCE does not have nvarchar(max) for now
|
||||
// SqlCE does not have nvarchar(max) for now
|
||||
if (dbTypes == SpecialDbType.NVARCHARMAX)
|
||||
{
|
||||
return "NTEXT";
|
||||
}
|
||||
|
||||
return base.GetSpecialDbType(dbTypes);
|
||||
}
|
||||
public override SqlDbType GetSqlDbType(DbType dbType)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using CSharpTest.Net.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
|
||||
@@ -6,7 +6,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
|
||||
/// <summary>
|
||||
/// Serializes/Deserializes data to BTree data source for <see cref="ContentData"/>
|
||||
/// </summary>
|
||||
internal class ContentDataSerializer : ISerializer<ContentData>
|
||||
public class ContentDataSerializer : ISerializer<ContentData>
|
||||
{
|
||||
public ContentDataSerializer(IDictionaryOfPropertyDataSerializer dictionaryOfPropertyDataSerializer = null)
|
||||
{
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
using System.Configuration;
|
||||
using System.Configuration;
|
||||
using CSharpTest.Net.Collections;
|
||||
using CSharpTest.Net.Serialization;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
|
||||
{
|
||||
internal class BTree
|
||||
public class BTree
|
||||
{
|
||||
public static BPlusTree<int, ContentNodeKit> GetTree(string filepath, bool exists, NuCacheSettings settings, ContentDataSerializer contentDataSerializer = null)
|
||||
{
|
||||
|
||||
@@ -14,12 +14,12 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
|
||||
/// <summary>
|
||||
/// Deserialize the data into a <see cref="ContentCacheDataModel"/>
|
||||
/// </summary>
|
||||
ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData);
|
||||
ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData, bool published);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the <see cref="ContentCacheDataModel"/>
|
||||
/// Serializes the <see cref="ContentCacheDataModel"/>
|
||||
/// </summary>
|
||||
ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model);
|
||||
ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model, bool published);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ using System.IO;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
|
||||
{
|
||||
internal interface IDictionaryOfPropertyDataSerializer
|
||||
public interface IDictionaryOfPropertyDataSerializer
|
||||
{
|
||||
IDictionary<string, PropertyData[]> ReadFrom(Stream stream);
|
||||
void WriteTo(IDictionary<string, PropertyData[]> value, Stream stream);
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
|
||||
DateFormatString = "o"
|
||||
};
|
||||
private readonly JsonNameTable _propertyNameTable = new DefaultJsonNameTable();
|
||||
public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData)
|
||||
public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData, bool published)
|
||||
{
|
||||
if (stringData == null && byteData != null)
|
||||
throw new NotSupportedException($"{typeof(JsonContentNestedDataSerializer)} does not support byte[] serialization");
|
||||
@@ -39,7 +39,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
|
||||
}
|
||||
}
|
||||
|
||||
public ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model)
|
||||
public ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model, bool published)
|
||||
{
|
||||
// note that numeric values (which are Int32) are serialized without their
|
||||
// type (eg "value":1234) and JsonConvert by default deserializes them as Int64
|
||||
|
||||
@@ -39,7 +39,8 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
|
||||
|
||||
_options = defaultOptions
|
||||
.WithResolver(resolver)
|
||||
.WithCompression(MessagePackCompression.Lz4BlockArray);
|
||||
.WithCompression(MessagePackCompression.Lz4BlockArray)
|
||||
.WithSecurity(MessagePackSecurity.UntrustedData);
|
||||
}
|
||||
|
||||
public string ToJson(byte[] bin)
|
||||
@@ -48,12 +49,12 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
|
||||
return json;
|
||||
}
|
||||
|
||||
public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData)
|
||||
public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData, bool published)
|
||||
{
|
||||
if (byteData != null)
|
||||
{
|
||||
var cacheModel = MessagePackSerializer.Deserialize<ContentCacheDataModel>(byteData, _options);
|
||||
Expand(content, cacheModel);
|
||||
Expand(content, cacheModel, published);
|
||||
return cacheModel;
|
||||
}
|
||||
else if (stringData != null)
|
||||
@@ -61,7 +62,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
|
||||
// NOTE: We don't really support strings but it's possible if manually used (i.e. tests)
|
||||
var bin = Convert.FromBase64String(stringData);
|
||||
var cacheModel = MessagePackSerializer.Deserialize<ContentCacheDataModel>(bin, _options);
|
||||
Expand(content, cacheModel);
|
||||
Expand(content, cacheModel, published);
|
||||
return cacheModel;
|
||||
}
|
||||
else
|
||||
@@ -70,9 +71,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
|
||||
}
|
||||
}
|
||||
|
||||
public ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model)
|
||||
public ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model, bool published)
|
||||
{
|
||||
Compress(content, model);
|
||||
Compress(content, model, published);
|
||||
var bytes = MessagePackSerializer.Serialize(model, _options);
|
||||
return new ContentCacheDataSerializationResult(null, bytes);
|
||||
}
|
||||
@@ -80,7 +81,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
|
||||
/// <summary>
|
||||
/// Used during serialization to compress properties
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="model"></param>
|
||||
/// <param name="published"></param>
|
||||
/// <remarks>
|
||||
/// This will essentially 'double compress' property data. The MsgPack data as a whole will already be compressed
|
||||
/// but this will go a step further and double compress property data so that it is stored in the nucache file
|
||||
@@ -88,11 +91,11 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
|
||||
/// read/decompressed as a string to be displayed on the front-end. This allows for potentially a significant
|
||||
/// memory savings but could also affect performance of first rendering pages while decompression occurs.
|
||||
/// </remarks>
|
||||
private void Compress(IReadOnlyContentBase content, ContentCacheDataModel model)
|
||||
private void Compress(IReadOnlyContentBase content, ContentCacheDataModel model, bool published)
|
||||
{
|
||||
foreach(var propertyAliasToData in model.PropertyData)
|
||||
{
|
||||
if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key))
|
||||
if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key, published))
|
||||
{
|
||||
foreach(var property in propertyAliasToData.Value.Where(x => x.Value != null && x.Value is string))
|
||||
{
|
||||
@@ -105,12 +108,14 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
|
||||
/// <summary>
|
||||
/// Used during deserialization to map the property data as lazy or expand the value
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="nestedData"></param>
|
||||
private void Expand(IReadOnlyContentBase content, ContentCacheDataModel nestedData)
|
||||
/// <param name="published"></param>
|
||||
private void Expand(IReadOnlyContentBase content, ContentCacheDataModel nestedData, bool published)
|
||||
{
|
||||
foreach (var propertyAliasToData in nestedData.PropertyData)
|
||||
{
|
||||
if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key))
|
||||
if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key,published))
|
||||
{
|
||||
foreach (var property in propertyAliasToData.Value.Where(x => x.Value != null))
|
||||
{
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
|
||||
{
|
||||
@@ -14,7 +13,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
|
||||
private readonly IMemberTypeService _memberTypeService;
|
||||
private readonly PropertyEditorCollection _propertyEditors;
|
||||
private readonly IPropertyCacheCompressionOptions _compressionOptions;
|
||||
private readonly ConcurrentDictionary<(int, string), bool> _isCompressedCache = new ConcurrentDictionary<(int, string), bool>();
|
||||
private readonly ConcurrentDictionary<(int, string, bool), bool> _isCompressedCache = new ConcurrentDictionary<(int, string, bool), bool>();
|
||||
|
||||
public MsgPackContentNestedDataSerializerFactory(
|
||||
IContentTypeService contentTypeService,
|
||||
|
||||
@@ -68,7 +68,20 @@ namespace Umbraco.Extensions
|
||||
throw new IndexOutOfRangeException();
|
||||
}
|
||||
});
|
||||
builder.Services.AddSingleton<IPropertyCacheCompressionOptions, NoopPropertyCacheCompressionOptions>();
|
||||
|
||||
builder.Services.AddSingleton<IPropertyCacheCompressionOptions>(s =>
|
||||
{
|
||||
IOptions<NuCacheSettings> options = s.GetRequiredService<IOptions<NuCacheSettings>>();
|
||||
|
||||
if (options.Value.NuCacheSerializerType == NuCacheSerializerType.MessagePack &&
|
||||
options.Value.UnPublishedContentCompression)
|
||||
{
|
||||
return new UnPublishedContentPropertyCacheCompressionOptions();
|
||||
}
|
||||
|
||||
return new NoopPropertyCacheCompressionOptions();
|
||||
});
|
||||
|
||||
builder.Services.AddSingleton(s => new ContentDataSerializer(new DictionaryOfPropertyDataSerializer()));
|
||||
|
||||
// add the NuCache health check (hidden from type finder)
|
||||
|
||||
@@ -415,7 +415,7 @@ AND cmsContentNu.nodeId IS NULL
|
||||
UrlSegment = content.GetUrlSegment(_shortStringHelper, _urlSegmentProviders)
|
||||
};
|
||||
|
||||
var serialized = serializer.Serialize(ReadOnlyContentBaseAdapter.Create(content), contentCacheData);
|
||||
var serialized = serializer.Serialize(ReadOnlyContentBaseAdapter.Create(content), contentCacheData, published);
|
||||
|
||||
var dto = new ContentNuDto
|
||||
{
|
||||
@@ -817,12 +817,13 @@ AND cmsContentNu.nodeId IS NULL
|
||||
}
|
||||
else
|
||||
{
|
||||
var deserializedContent = serializer.Deserialize(dto, dto.EditData, dto.EditDataRaw);
|
||||
bool published = false;
|
||||
var deserializedContent = serializer.Deserialize(dto, dto.EditData, dto.EditDataRaw, published);
|
||||
|
||||
d = new ContentData
|
||||
{
|
||||
Name = dto.EditName,
|
||||
Published = false,
|
||||
Published = published,
|
||||
TemplateId = dto.EditTemplateId,
|
||||
VersionId = dto.VersionId,
|
||||
VersionDate = dto.EditVersionDate,
|
||||
@@ -847,13 +848,14 @@ AND cmsContentNu.nodeId IS NULL
|
||||
}
|
||||
else
|
||||
{
|
||||
var deserializedContent = serializer.Deserialize(dto, dto.PubData, dto.PubDataRaw);
|
||||
bool published = true;
|
||||
var deserializedContent = serializer.Deserialize(dto, dto.PubData, dto.PubDataRaw, published);
|
||||
|
||||
p = new ContentData
|
||||
{
|
||||
Name = dto.PubName,
|
||||
UrlSegment = deserializedContent.UrlSegment,
|
||||
Published = true,
|
||||
Published = published,
|
||||
TemplateId = dto.PubTemplateId,
|
||||
VersionId = dto.VersionId,
|
||||
VersionDate = dto.PubVersionDate,
|
||||
@@ -883,12 +885,13 @@ AND cmsContentNu.nodeId IS NULL
|
||||
if (dto.EditData == null && dto.EditDataRaw == null)
|
||||
throw new InvalidOperationException("No data for media " + dto.Id);
|
||||
|
||||
var deserializedMedia = serializer.Deserialize(dto, dto.EditData, dto.EditDataRaw);
|
||||
bool published = true;
|
||||
var deserializedMedia = serializer.Deserialize(dto, dto.EditData, dto.EditDataRaw, published);
|
||||
|
||||
var p = new ContentData
|
||||
{
|
||||
Name = dto.EditName,
|
||||
Published = true,
|
||||
Published = published,
|
||||
TemplateId = -1,
|
||||
VersionId = dto.VersionId,
|
||||
VersionDate = dto.EditVersionDate,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MessagePack" Version="2.2.85" />
|
||||
<PackageReference Include="K4os.Compression.LZ4" Version="1.2.6" />
|
||||
<PackageReference Include="MessagePack" Version="2.3.75" />
|
||||
<PackageReference Include="K4os.Compression.LZ4" Version="1.2.12" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="Umbraco.Code" Version="1.1.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
16
src/Umbraco.TestData/Configuration/TestDataSettings.cs
Normal file
16
src/Umbraco.TestData/Configuration/TestDataSettings.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace Umbraco.TestData.Configuration
|
||||
{
|
||||
public class TestDataSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the test data generation is enabled.
|
||||
/// </summary>
|
||||
public bool Enabled { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether persisted local database cache files for content and media are disabled.
|
||||
/// </summary>
|
||||
/// <value>The URL path.</value>
|
||||
public bool IgnoreLocalDb { get; set; } = false;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user